github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/unqlite/unqlite.c (about)

     1  /*
     2   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
     3   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
     4   * Version 1.1.6
     5   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
     6   * please contact Symisc Systems via:
     7   *       legal@symisc.net
     8   *       licensing@symisc.net
     9   *       contact@symisc.net
    10   * or visit:
    11   *      http://unqlite.org/licensing.html
    12   */
    13  /*
    14   * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
    15   * All rights reserved.
    16   *
    17   * Redistribution and use in source and binary forms, with or without
    18   * modification, are permitted provided that the following conditions
    19   * are met:
    20   * 1. Redistributions of source code must retain the above copyright
    21   *    notice, this list of conditions and the following disclaimer.
    22   * 2. Redistributions in binary form must reproduce the above copyright
    23   *    notice, this list of conditions and the following disclaimer in the
    24   *    documentation and/or other materials provided with the distribution.
    25   *
    26   * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
    27   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    28   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
    29   * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
    30   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    31   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    32   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
    33   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
    34   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
    35   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
    36   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    37   */
    38  /*
    39   * $SymiscID: unqlite.c v1.1.6 Unix|Win32/64 2013-07-08 03:38:18 stable <chm@symisc.net> $ 
    40   */
    41  /* This file is an amalgamation of many separate C source files from unqlite version 1.1.6
    42   * By combining all the individual C code files into this single large file, the entire code
    43   * can be compiled as a single translation unit. This allows many compilers to do optimization's
    44   * that would not be possible if the files were compiled separately. Performance improvements
    45   * are commonly seen when unqlite is compiled as a single translation unit.
    46   *
    47   * This file is all you need to compile unqlite. To use unqlite in other programs, you need
    48   * this file and the "unqlite.h" header file that defines the programming interface to the 
    49   * unqlite engine.(If you do not have the "unqlite.h" header file at hand, you will find
    50   * a copy embedded within the text of this file.Search for "Header file: <unqlite.h>" to find
    51   * the start of the embedded unqlite.h header file.) Additional code files may be needed if
    52   * you want a wrapper to interface unqlite with your choice of programming language.
    53   * To get the official documentation, please visit http://unqlite.org/
    54   */
    55   /*
    56    * Make the sure the following directive is defined in the amalgamation build.
    57    */
    58   #ifndef UNQLITE_AMALGAMATION
    59   #define UNQLITE_AMALGAMATION
    60   #define JX9_AMALGAMATION
    61   /* Marker for routines not intended for external use */
    62   #define JX9_PRIVATE static
    63   #endif /* UNQLITE_AMALGAMATION */
    64  /*
    65   * Embedded header file for unqlite: <unqlite.h>
    66   */
    67  /*
    68   * ----------------------------------------------------------
    69   * File: unqlite.h
    70   * MD5: d26e9847c6587edbbb183d0115d172cb
    71   * ----------------------------------------------------------
    72   */
    73  /* This file was automatically generated.  Do not edit (Except for compile time directives)! */ 
    74  #ifndef _UNQLITE_H_
    75  #define _UNQLITE_H_
    76  /*
    77   * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
    78   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
    79   * Version 1.1.6
    80   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
    81   * please contact Symisc Systems via:
    82   *       legal@symisc.net
    83   *       licensing@symisc.net
    84   *       contact@symisc.net
    85   * or visit:
    86   *      http://unqlite.org/licensing.html
    87   */
    88  /*
    89   * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
    90   * All rights reserved.
    91   *
    92   * Redistribution and use in source and binary forms, with or without
    93   * modification, are permitted provided that the following conditions
    94   * are met:
    95   * 1. Redistributions of source code must retain the above copyright
    96   *    notice, this list of conditions and the following disclaimer.
    97   * 2. Redistributions in binary form must reproduce the above copyright
    98   *    notice, this list of conditions and the following disclaimer in the
    99   *    documentation and/or other materials provided with the distribution.
   100   *
   101   * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
   102   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   103   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
   104   * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
   105   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   106   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   107   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   108   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
   109   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   110   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   111   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   112   */
   113   /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable <chm@symisc.net> $ */
   114  #include <stdarg.h> /* needed for the definition of va_list */
   115  /*
   116   * Compile time engine version, signature, identification in the symisc source tree
   117   * and copyright notice.
   118   * Each macro have an equivalent C interface associated with it that provide the same
   119   * information but are associated with the library instead of the header file.
   120   * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
   121   * [unqlite_lib_copyright()] for more information.
   122   */
   123  /*
   124   * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
   125   * that is the unqlite version in the format "X.Y.Z" where X is the major
   126   * version number and Y is the minor version number and Z is the release
   127   * number.
   128   */
   129  #define UNQLITE_VERSION "1.1.6"
   130  /*
   131   * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer
   132   * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
   133   * numbers used in [UNQLITE_VERSION].
   134   */
   135  #define UNQLITE_VERSION_NUMBER 1001006
   136  /*
   137   * The UNQLITE_SIG C preprocessor macro evaluates to a string
   138   * literal which is the public signature of the unqlite engine.
   139   * This signature could be included for example in a host-application
   140   * generated Server MIME header as follows:
   141   *   Server: YourWebServer/x.x unqlite/x.x.x \r\n
   142   */
   143  #define UNQLITE_SIG "unqlite/1.1.6"
   144  /*
   145   * UnQLite identification in the Symisc source tree:
   146   * Each particular check-in of a particular software released
   147   * by symisc systems have an unique identifier associated with it.
   148   * This macro hold the one associated with unqlite.
   149   */
   150  #define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
   151  /*
   152   * Copyright notice.
   153   * If you have any questions about the licensing situation, please
   154   * visit http://unqlite.org/licensing.html
   155   * or contact Symisc Systems via:
   156   *   legal@symisc.net
   157   *   licensing@symisc.net
   158   *   contact@symisc.net
   159   */
   160  #define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2013, http://unqlite.org/"
   161  /* Make sure we can call this stuff from C++ */
   162  #ifdef __cplusplus
   163  extern "C" { 
   164  #endif
   165  /* Forward declaration to public objects */
   166  typedef struct unqlite_io_methods unqlite_io_methods;
   167  typedef struct unqlite_kv_methods unqlite_kv_methods;
   168  typedef struct unqlite_kv_engine unqlite_kv_engine;
   169  typedef struct jx9_io_stream unqlite_io_stream;
   170  typedef struct jx9_context unqlite_context;
   171  typedef struct jx9_value unqlite_value;
   172  typedef struct unqlite_vfs unqlite_vfs;
   173  typedef struct unqlite_vm unqlite_vm;
   174  typedef struct unqlite unqlite;
   175  /*
   176   * ------------------------------
   177   * Compile time directives
   178   * ------------------------------
   179   * For most purposes, UnQLite can be built just fine using the default compilation options.
   180   * However, if required, the compile-time options documented below can be used to omit UnQLite
   181   * features (resulting in a smaller compiled library size) or to change the default values
   182   * of some parameters.
   183   * Every effort has been made to ensure that the various combinations of compilation options
   184   * work harmoniously and produce a working library.
   185   *
   186   * UNQLITE_ENABLE_THREADS
   187   *  This option controls whether or not code is included in UnQLite to enable it to operate
   188   *  safely in a multithreaded environment. The default is not. All mutexing code is omitted
   189   *  and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
   190   *  UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
   191   *  and it is safe to share the same virtual machine and engine handle between two or more threads.
   192   *  The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
   193   *  interface.
   194   *  When UnQLite has been compiled with threading support then the threading mode can be altered
   195   * at run-time using the unqlite_lib_config() interface together with one of these verbs:
   196   *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
   197   *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
   198   *  Platforms others than Windows and UNIX systems must install their own mutex subsystem via 
   199   *  unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
   200   *  Otherwise the library is not threadsafe.
   201   *  Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
   202   *
   203   * Options To Omit/Enable Features
   204   *
   205   * The following options can be used to reduce the size of the compiled library by omitting optional
   206   * features. This is probably only useful in embedded systems where space is especially tight, as even
   207   * with all features included the UnQLite library is relatively small. Don't forget to tell your
   208   * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler
   209   * to optimize for size usually has a much larger impact on library footprint than employing
   210   * any of these compile-time options.
   211   *
   212   * JX9_DISABLE_BUILTIN_FUNC
   213   *  Jx9 is shipped with more than 312 built-in functions suitable for most purposes like 
   214   *  string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
   215   *  and so forth.
   216   *  If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
   217   *  Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted
   218   *  from the build and are not affected by this directive.
   219   *
   220   * JX9_ENABLE_MATH_FUNC
   221   *  If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc.
   222   *  are included in the build. Note that you may need to link UnQLite with the math library in same
   223   *  Linux/BSD flavor (i.e: -lm).
   224   *
   225   * JX9_DISABLE_DISK_IO
   226   *  If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
   227   *  sleep(), etc. are omitted from the build.
   228   *
   229   * UNQLITE_ENABLE_JX9_HASH_IO
   230   * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
   231   * are included in the build.
   232   */
   233  /* Symisc public definitions */
   234  #if !defined(SYMISC_STANDARD_DEFS)
   235  #define SYMISC_STANDARD_DEFS
   236  #if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE)
   237  /* Windows Systems */
   238  #if !defined(__WINNT__)
   239  #define __WINNT__
   240  #endif 
   241  /*
   242   * Determine if we are dealing with WindowsCE - which has a much
   243   * reduced API.
   244   */
   245  #if defined(_WIN32_WCE)
   246  #ifndef __WIN_CE__
   247  #define __WIN_CE__
   248  #endif /* __WIN_CE__ */
   249  #endif /* _WIN32_WCE */
   250  #else
   251  /*
   252   * By default we will assume that we are compiling on a UNIX systems.
   253   * Otherwise the OS_OTHER directive must be defined.
   254   */
   255  #if !defined(OS_OTHER)
   256  #if !defined(__UNIXES__)
   257  #define __UNIXES__
   258  #endif /* __UNIXES__ */
   259  #else
   260  #endif /* OS_OTHER */
   261  #endif /* __WINNT__/__UNIXES__ */
   262  #if defined(_MSC_VER) || defined(__BORLANDC__)
   263  typedef signed __int64     sxi64; /* 64 bits(8 bytes) signed int64 */
   264  typedef unsigned __int64   sxu64; /* 64 bits(8 bytes) unsigned int64 */
   265  #else
   266  typedef signed long long int   sxi64; /* 64 bits(8 bytes) signed int64 */
   267  typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */
   268  #endif /* _MSC_VER */
   269  /* Signature of the consumer routine */
   270  typedef int (*ProcConsumer)(const void *, unsigned int, void *);
   271  /* Forward reference */
   272  typedef struct SyMutexMethods SyMutexMethods;
   273  typedef struct SyMemMethods SyMemMethods;
   274  typedef struct SyString SyString;
   275  typedef struct syiovec syiovec;
   276  typedef struct SyMutex SyMutex;
   277  typedef struct Sytm Sytm;
   278  /* Scatter and gather array. */
   279  struct syiovec
   280  {
   281  #if defined (__WINNT__)
   282  	/* Same fields type and offset as WSABUF structure defined one winsock2 header */
   283  	unsigned long nLen;
   284  	char *pBase;
   285  #else
   286  	void *pBase;
   287  	unsigned long nLen;
   288  #endif
   289  };
   290  struct SyString
   291  {
   292  	const char *zString;  /* Raw string (may not be null terminated) */
   293  	unsigned int nByte;   /* Raw string length */
   294  };
   295  /* Time structure. */
   296  struct Sytm
   297  {
   298    int tm_sec;     /* seconds (0 - 60) */
   299    int tm_min;     /* minutes (0 - 59) */
   300    int tm_hour;    /* hours (0 - 23) */
   301    int tm_mday;    /* day of month (1 - 31) */
   302    int tm_mon;     /* month of year (0 - 11) */
   303    int tm_year;    /* year + 1900 */
   304    int tm_wday;    /* day of week (Sunday = 0) */
   305    int tm_yday;    /* day of year (0 - 365) */
   306    int tm_isdst;   /* is summer time in effect? */
   307    char *tm_zone;  /* abbreviation of timezone name */
   308    long tm_gmtoff; /* offset from UTC in seconds */
   309  };
   310  /* Convert a tm structure (struct tm *) found in <time.h> to a Sytm structure */
   311  #define STRUCT_TM_TO_SYTM(pTM, pSYTM) \
   312  	(pSYTM)->tm_hour = (pTM)->tm_hour;\
   313  	(pSYTM)->tm_min	 = (pTM)->tm_min;\
   314  	(pSYTM)->tm_sec	 = (pTM)->tm_sec;\
   315  	(pSYTM)->tm_mon	 = (pTM)->tm_mon;\
   316  	(pSYTM)->tm_mday = (pTM)->tm_mday;\
   317  	(pSYTM)->tm_year = (pTM)->tm_year + 1900;\
   318  	(pSYTM)->tm_yday = (pTM)->tm_yday;\
   319  	(pSYTM)->tm_wday = (pTM)->tm_wday;\
   320  	(pSYTM)->tm_isdst = (pTM)->tm_isdst;\
   321  	(pSYTM)->tm_gmtoff = 0;\
   322  	(pSYTM)->tm_zone = 0;
   323  
   324  /* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */
   325  #define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \
   326  	 (pSYTM)->tm_hour = (pSYSTIME)->wHour;\
   327  	 (pSYTM)->tm_min  = (pSYSTIME)->wMinute;\
   328  	 (pSYTM)->tm_sec  = (pSYSTIME)->wSecond;\
   329  	 (pSYTM)->tm_mon  = (pSYSTIME)->wMonth - 1;\
   330  	 (pSYTM)->tm_mday = (pSYSTIME)->wDay;\
   331  	 (pSYTM)->tm_year = (pSYSTIME)->wYear;\
   332  	 (pSYTM)->tm_yday = 0;\
   333  	 (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\
   334  	 (pSYTM)->tm_gmtoff = 0;\
   335  	 (pSYTM)->tm_isdst = -1;\
   336  	 (pSYTM)->tm_zone = 0;
   337  
   338  /* Dynamic memory allocation methods. */
   339  struct SyMemMethods 
   340  {
   341  	void * (*xAlloc)(unsigned int);          /* [Required:] Allocate a memory chunk */
   342  	void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */
   343  	void   (*xFree)(void *);                 /* [Required:] Release a memory chunk */
   344  	unsigned int  (*xChunkSize)(void *);     /* [Optional:] Return chunk size */
   345  	int    (*xInit)(void *);                 /* [Optional:] Initialization callback */
   346  	void   (*xRelease)(void *);              /* [Optional:] Release callback */
   347  	void  *pUserData;                        /* [Optional:] First argument to xInit() and xRelease() */
   348  };
   349  /* Out of memory callback signature. */
   350  typedef int (*ProcMemError)(void *);
   351  /* Mutex methods. */
   352  struct SyMutexMethods 
   353  {
   354  	int (*xGlobalInit)(void);		/* [Optional:] Global mutex initialization */
   355  	void  (*xGlobalRelease)(void);	/* [Optional:] Global Release callback () */
   356  	SyMutex * (*xNew)(int);	        /* [Required:] Request a new mutex */
   357  	void  (*xRelease)(SyMutex *);	/* [Optional:] Release a mutex  */
   358  	void  (*xEnter)(SyMutex *);	    /* [Required:] Enter mutex */
   359  	int (*xTryEnter)(SyMutex *);    /* [Optional:] Try to enter a mutex */
   360  	void  (*xLeave)(SyMutex *);	    /* [Required:] Leave a locked mutex */
   361  };
   362  #if defined (_MSC_VER) || defined (__MINGW32__) ||  defined (__GNUC__) && defined (__declspec)
   363  #define SX_APIIMPORT	__declspec(dllimport)
   364  #define SX_APIEXPORT	__declspec(dllexport)
   365  #else
   366  #define	SX_APIIMPORT
   367  #define	SX_APIEXPORT
   368  #endif
   369  /* Standard return values from Symisc public interfaces */
   370  #define SXRET_OK       0      /* Not an error */	
   371  #define SXERR_MEM      (-1)   /* Out of memory */
   372  #define SXERR_IO       (-2)   /* IO error */
   373  #define SXERR_EMPTY    (-3)   /* Empty field */
   374  #define SXERR_LOCKED   (-4)   /* Locked operation */
   375  #define SXERR_ORANGE   (-5)   /* Out of range value */
   376  #define SXERR_NOTFOUND (-6)   /* Item not found */
   377  #define SXERR_LIMIT    (-7)   /* Limit reached */
   378  #define SXERR_MORE     (-8)   /* Need more input */
   379  #define SXERR_INVALID  (-9)   /* Invalid parameter */
   380  #define SXERR_ABORT    (-10)  /* User callback request an operation abort */
   381  #define SXERR_EXISTS   (-11)  /* Item exists */
   382  #define SXERR_SYNTAX   (-12)  /* Syntax error */
   383  #define SXERR_UNKNOWN  (-13)  /* Unknown error */
   384  #define SXERR_BUSY     (-14)  /* Busy operation */
   385  #define SXERR_OVERFLOW (-15)  /* Stack or buffer overflow */
   386  #define SXERR_WILLBLOCK (-16) /* Operation will block */
   387  #define SXERR_NOTIMPLEMENTED  (-17) /* Operation not implemented */
   388  #define SXERR_EOF      (-18) /* End of input */
   389  #define SXERR_PERM     (-19) /* Permission error */
   390  #define SXERR_NOOP     (-20) /* No-op */	
   391  #define SXERR_FORMAT   (-21) /* Invalid format */
   392  #define SXERR_NEXT     (-22) /* Not an error */
   393  #define SXERR_OS       (-23) /* System call return an error */
   394  #define SXERR_CORRUPT  (-24) /* Corrupted pointer */
   395  #define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */
   396  #define SXERR_NOMATCH  (-26) /* No match */
   397  #define SXERR_RESET    (-27) /* Operation reset */
   398  #define SXERR_DONE     (-28) /* Not an error */
   399  #define SXERR_SHORT    (-29) /* Buffer too short */
   400  #define SXERR_PATH     (-30) /* Path error */
   401  #define SXERR_TIMEOUT  (-31) /* Timeout */
   402  #define SXERR_BIG      (-32) /* Too big for processing */
   403  #define SXERR_RETRY    (-33) /* Retry your call */
   404  #define SXERR_IGNORE   (-63) /* Ignore */
   405  #endif /* SYMISC_PUBLIC_DEFS */
   406  /* 
   407   * Marker for exported interfaces. 
   408   */
   409  #define UNQLITE_APIEXPORT SX_APIEXPORT
   410  /*
   411   * If compiling for a processor that lacks floating point
   412   * support, substitute integer for floating-point.
   413   */
   414  #ifdef UNQLITE_OMIT_FLOATING_POINT
   415  typedef sxi64 uqlite_real;
   416  #else
   417  typedef double unqlite_real;
   418  #endif
   419  typedef sxi64 unqlite_int64;
   420  /* Standard UnQLite return values */
   421  #define UNQLITE_OK      SXRET_OK      /* Successful result */
   422  /* Beginning of error codes */
   423  #define UNQLITE_NOMEM    SXERR_MEM     /* Out of memory */
   424  #define UNQLITE_ABORT    SXERR_ABORT   /* Another thread have released this instance */
   425  #define UNQLITE_IOERR    SXERR_IO      /* IO error */
   426  #define UNQLITE_CORRUPT  SXERR_CORRUPT /* Corrupt pointer */
   427  #define UNQLITE_LOCKED   SXERR_LOCKED  /* Forbidden Operation */ 
   428  #define UNQLITE_BUSY	 SXERR_BUSY    /* The database file is locked */
   429  #define UNQLITE_DONE	 SXERR_DONE    /* Operation done */
   430  #define UNQLITE_PERM     SXERR_PERM    /* Permission error */
   431  #define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
   432  #define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
   433  #define UNQLITE_NOOP     SXERR_NOOP     /* No such method */
   434  #define UNQLITE_INVALID  SXERR_INVALID  /* Invalid parameter */
   435  #define UNQLITE_EOF      SXERR_EOF      /* End Of Input */
   436  #define UNQLITE_UNKNOWN  SXERR_UNKNOWN  /* Unknown configuration option */
   437  #define UNQLITE_LIMIT    SXERR_LIMIT    /* Database limit reached */
   438  #define UNQLITE_EXISTS   SXERR_EXISTS   /* Record exists */
   439  #define UNQLITE_EMPTY    SXERR_EMPTY    /* Empty record */
   440  #define UNQLITE_COMPILE_ERR (-70)       /* Compilation error */
   441  #define UNQLITE_VM_ERR      (-71)       /* Virtual machine error */
   442  #define UNQLITE_FULL        (-73)       /* Full database (unlikely) */
   443  #define UNQLITE_CANTOPEN    (-74)       /* Unable to open the database file */
   444  #define UNQLITE_READ_ONLY   (-75)       /* Read only Key/Value storage engine */
   445  #define UNQLITE_LOCKERR     (-76)       /* Locking protocol error */
   446  /* end-of-error-codes */
   447  /*
   448   * Database Handle Configuration Commands.
   449   *
   450   * The following set of constants are the available configuration verbs that can
   451   * be used by the host-application to configure an UnQLite database handle.
   452   * These constants must be passed as the second argument to [unqlite_config()].
   453   *
   454   * Each options require a variable number of arguments.
   455   * The [unqlite_config()] interface will return UNQLITE_OK on success, any other
   456   * return value indicates failure.
   457   * For a full discussion on the configuration verbs and their expected 
   458   * parameters, please refer to this page:
   459   *      http://unqlite.org/c_api/unqlite_config.html
   460   */
   461  #define UNQLITE_CONFIG_JX9_ERR_LOG         1  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
   462  #define UNQLITE_CONFIG_MAX_PAGE_CACHE      2  /* ONE ARGUMENT: int nMaxPage */
   463  #define UNQLITE_CONFIG_ERR_LOG             3  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
   464  #define UNQLITE_CONFIG_KV_ENGINE           4  /* ONE ARGUMENT: const char *zKvName */
   465  #define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5  /* NO ARGUMENTS */
   466  #define UNQLITE_CONFIG_GET_KV_NAME         6  /* ONE ARGUMENT: const char **pzPtr */
   467  /*
   468   * UnQLite/Jx9 Virtual Machine Configuration Commands.
   469   *
   470   * The following set of constants are the available configuration verbs that can
   471   * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine.
   472   * These constants must be passed as the second argument to the [unqlite_vm_config()] 
   473   * interface.
   474   * Each options require a variable number of arguments.
   475   * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
   476   * value indicates failure.
   477   * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
   478   * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
   479   * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
   480   * For a full discussion on the configuration verbs and their expected parameters, please
   481   * refer to this page:
   482   *      http://unqlite.org/c_api/unqlite_vm_config.html
   483   */
   484  #define UNQLITE_VM_CONFIG_OUTPUT           1  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
   485  #define UNQLITE_VM_CONFIG_IMPORT_PATH      2  /* ONE ARGUMENT: const char *zIncludePath */
   486  #define UNQLITE_VM_CONFIG_ERR_REPORT       3  /* NO ARGUMENTS: Report all run-time errors in the VM output */
   487  #define UNQLITE_VM_CONFIG_RECURSION_DEPTH  4  /* ONE ARGUMENT: int nMaxDepth */
   488  #define UNQLITE_VM_OUTPUT_LENGTH           5  /* ONE ARGUMENT: unsigned int *pLength */
   489  #define UNQLITE_VM_CONFIG_CREATE_VAR       6  /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
   490  #define UNQLITE_VM_CONFIG_HTTP_REQUEST     7  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
   491  #define UNQLITE_VM_CONFIG_SERVER_ATTR      8  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
   492  #define UNQLITE_VM_CONFIG_ENV_ATTR         9  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
   493  #define UNQLITE_VM_CONFIG_EXEC_VALUE      10  /* ONE ARGUMENT: unqlite_value **ppValue */
   494  #define UNQLITE_VM_CONFIG_IO_STREAM       11  /* ONE ARGUMENT: const unqlite_io_stream *pStream */
   495  #define UNQLITE_VM_CONFIG_ARGV_ENTRY      12  /* ONE ARGUMENT: const char *zValue */
   496  #define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  13  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
   497  /*
   498   * Storage engine configuration commands.
   499   *
   500   * The following set of constants are the available configuration verbs that can
   501   * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
   502   * These constants must be passed as the first argument to [unqlite_kv_config()].
   503   * Each options require a variable number of arguments.
   504   * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return
   505   * value indicates failure.
   506   * For a full discussion on the configuration verbs and their expected parameters, please
   507   * refer to this page:
   508   *      http://unqlite.org/c_api/unqlite_kv_config.html
   509   */
   510  #define UNQLITE_KV_CONFIG_HASH_FUNC  1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
   511  #define UNQLITE_KV_CONFIG_CMP_FUNC   2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
   512  /*
   513   * Global Library Configuration Commands.
   514   *
   515   * The following set of constants are the available configuration verbs that can
   516   * be used by the host-application to configure the whole library.
   517   * These constants must be passed as the first argument to [unqlite_lib_config()].
   518   *
   519   * Each options require a variable number of arguments.
   520   * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
   521   * value indicates failure.
   522   * Notes:
   523   * The default configuration is recommended for most applications and so the call to
   524   * [unqlite_lib_config()] is usually not necessary. It is provided to support rare 
   525   * applications with unusual needs. 
   526   * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
   527   * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
   528   * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
   529   * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
   530   * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
   531   * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
   532   * For a full discussion on the configuration verbs and their expected parameters, please
   533   * refer to this page:
   534   *      http://unqlite.org/c_api/unqlite_lib.html
   535   */
   536  #define UNQLITE_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ 
   537  #define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
   538  #define UNQLITE_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ 
   539  #define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */ 
   540  #define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */ 
   541  #define UNQLITE_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
   542  #define UNQLITE_LIB_CONFIG_STORAGE_ENGINE         7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
   543  #define UNQLITE_LIB_CONFIG_PAGE_SIZE              8 /* ONE ARGUMENT: int iPageSize */
   544  /*
   545   * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
   546   * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
   547   */
   548  #define UNQLITE_OPEN_READONLY         0x00000001  /* Read only mode. Ok for [unqlite_open] */
   549  #define UNQLITE_OPEN_READWRITE        0x00000002  /* Ok for [unqlite_open] */
   550  #define UNQLITE_OPEN_CREATE           0x00000004  /* Ok for [unqlite_open] */
   551  #define UNQLITE_OPEN_EXCLUSIVE        0x00000008  /* VFS only */
   552  #define UNQLITE_OPEN_TEMP_DB          0x00000010  /* VFS only */
   553  #define UNQLITE_OPEN_NOMUTEX          0x00000020  /* Ok for [unqlite_open] */
   554  #define UNQLITE_OPEN_OMIT_JOURNALING  0x00000040  /* Omit journaling for this database. Ok for [unqlite_open] */
   555  #define UNQLITE_OPEN_IN_MEMORY        0x00000080  /* An in memory database. Ok for [unqlite_open]*/
   556  #define UNQLITE_OPEN_MMAP             0x00000100  /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
   557  /*
   558   * Synchronization Type Flags
   559   *
   560   * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
   561   * a combination of these integer values as the second argument.
   562   *
   563   * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
   564   * needs to flush data to mass storage.  Inode information need not be flushed.
   565   * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
   566   * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
   567   * Mac OS X style fullsync instead of fsync().
   568   */
   569  #define UNQLITE_SYNC_NORMAL        0x00002
   570  #define UNQLITE_SYNC_FULL          0x00003
   571  #define UNQLITE_SYNC_DATAONLY      0x00010
   572  /*
   573   * File Locking Levels
   574   *
   575   * UnQLite uses one of these integer values as the second
   576   * argument to calls it makes to the xLock() and xUnlock() methods
   577   * of an [unqlite_io_methods] object.
   578   */
   579  #define UNQLITE_LOCK_NONE          0
   580  #define UNQLITE_LOCK_SHARED        1
   581  #define UNQLITE_LOCK_RESERVED      2
   582  #define UNQLITE_LOCK_PENDING       3
   583  #define UNQLITE_LOCK_EXCLUSIVE     4
   584  /*
   585   * CAPIREF: OS Interface: Open File Handle
   586   *
   587   * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
   588   * layer.
   589   * Individual OS interface implementations will want to subclass this object by appending
   590   * additional fields for their own use. The pMethods entry is a pointer to an
   591   * [unqlite_io_methods] object that defines methods for performing
   592   * I/O operations on the open file.
   593  */
   594  typedef struct unqlite_file unqlite_file;
   595  struct unqlite_file {
   596    const unqlite_io_methods *pMethods;  /* Methods for an open file. MUST BE FIRST */
   597  };
   598  /*
   599   * CAPIREF: OS Interface: File Methods Object
   600   *
   601   * Every file opened by the [unqlite_vfs] xOpen method populates an
   602   * [unqlite_file] object (or, more commonly, a subclass of the
   603   * [unqlite_file] object) with a pointer to an instance of this object.
   604   * This object defines the methods used to perform various operations
   605   * against the open file represented by the [unqlite_file] object.
   606   *
   607   * If the xOpen method sets the unqlite_file.pMethods element 
   608   * to a non-NULL pointer, then the unqlite_io_methods.xClose method
   609   * may be invoked even if the xOpen reported that it failed.  The
   610   * only way to prevent a call to xClose following a failed xOpen
   611   * is for the xOpen to set the unqlite_file.pMethods element to NULL.
   612   *
   613   * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
   614   * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
   615   * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
   616   * flag may be ORed in to indicate that only the data of the file
   617   * and not its inode needs to be synced.
   618   *
   619   * The integer values to xLock() and xUnlock() are one of
   620   *
   621   * UNQLITE_LOCK_NONE
   622   * UNQLITE_LOCK_SHARED
   623   * UNQLITE_LOCK_RESERVED
   624   * UNQLITE_LOCK_PENDING
   625   * UNQLITE_LOCK_EXCLUSIVE
   626   * 
   627   * xLock() increases the lock. xUnlock() decreases the lock.
   628   * The xCheckReservedLock() method checks whether any database connection,
   629   * either in this process or in some other process, is holding a RESERVED,
   630   * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
   631   * and false otherwise.
   632   * 
   633   * The xSectorSize() method returns the sector size of the device that underlies
   634   * the file. The sector size is the minimum write that can be performed without
   635   * disturbing other bytes in the file.
   636   *
   637   */
   638  struct unqlite_io_methods {
   639    int iVersion;                 /* Structure version number (currently 1) */
   640    int (*xClose)(unqlite_file*);
   641    int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
   642    int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
   643    int (*xTruncate)(unqlite_file*, unqlite_int64 size);
   644    int (*xSync)(unqlite_file*, int flags);
   645    int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
   646    int (*xLock)(unqlite_file*, int);
   647    int (*xUnlock)(unqlite_file*, int);
   648    int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
   649    int (*xSectorSize)(unqlite_file*);
   650  };
   651  /*
   652   * CAPIREF: OS Interface Object
   653   *
   654   * An instance of the unqlite_vfs object defines the interface between
   655   * the UnQLite core and the underlying operating system.  The "vfs"
   656   * in the name of the object stands for "Virtual File System".
   657   *
   658   * Only a single vfs can be registered within the UnQLite core.
   659   * Vfs registration is done using the [unqlite_lib_config()] interface
   660   * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS.
   661   * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users
   662   * does not have to worry about registering and installing a vfs since UnQLite
   663   * come with a built-in vfs for these platforms that implements most the methods
   664   * defined below.
   665   *
   666   * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
   667   * must register their own vfs in order to be able to use the UnQLite library.
   668   *
   669   * The value of the iVersion field is initially 1 but may be larger in
   670   * future versions of UnQLite. 
   671   *
   672   * The szOsFile field is the size of the subclassed [unqlite_file] structure
   673   * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
   674   * 
   675   * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
   676   * structure passed as the third argument to xOpen. The xOpen method does not have to
   677   * allocate the structure; it should just fill it in. Note that the xOpen method must
   678   * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
   679   * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
   680   * element will be valid after xOpen returns regardless of the success or failure of the
   681   * xOpen call.
   682   *
   683   */
   684  struct unqlite_vfs {
   685    const char *zName;       /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
   686    int iVersion;            /* Structure version number (currently 1) */
   687    int szOsFile;            /* Size of subclassed unqlite_file */
   688    int mxPathname;          /* Maximum file pathname length */
   689    int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
   690    int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
   691    int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
   692    int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
   693    int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
   694    int (*xSleep)(unqlite_vfs*, int microseconds);
   695    int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
   696    int (*xGetLastError)(unqlite_vfs*, int, char *);
   697  };
   698  /*
   699   * Flags for the xAccess VFS method
   700   *
   701   * These integer constants can be used as the third parameter to
   702   * the xAccess method of an [unqlite_vfs] object.  They determine
   703   * what kind of permissions the xAccess method is looking for.
   704   * With UNQLITE_ACCESS_EXISTS, the xAccess method
   705   * simply checks whether the file exists.
   706   * With UNQLITE_ACCESS_READWRITE, the xAccess method
   707   * checks whether the named directory is both readable and writable
   708   * (in other words, if files can be added, removed, and renamed within
   709   * the directory).
   710   * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
   711   * [temp_store_directory pragma], though this could change in a future
   712   * release of UnQLite.
   713   * With UNQLITE_ACCESS_READ, the xAccess method
   714   * checks whether the file is readable.  The UNQLITE_ACCESS_READ constant is
   715   * currently unused, though it might be used in a future release of
   716   * UnQLite.
   717   */
   718  #define UNQLITE_ACCESS_EXISTS    0
   719  #define UNQLITE_ACCESS_READWRITE 1   
   720  #define UNQLITE_ACCESS_READ      2 
   721  /*
   722   * The type used to represent a page number.  The first page in a file
   723   * is called page 1.  0 is used to represent "not a page".
   724   * A page number is an unsigned 64-bit integer.
   725   */
   726  typedef sxu64 pgno;
   727  /*
   728   * A database disk page is represented by an instance
   729   * of the follwoing structure.
   730   */
   731  typedef struct unqlite_page unqlite_page;
   732  struct unqlite_page
   733  {
   734    unsigned char *zData;       /* Content of this page */
   735    void *pUserData;            /* Extra content */
   736    pgno pgno;                  /* Page number for this page */
   737  };
   738  /*
   739   * UnQLite handle to the underlying Key/Value Storage Engine (See below).
   740   */
   741  typedef void * unqlite_kv_handle;
   742  /*
   743   * UnQLite pager IO methods.
   744   *
   745   * An instance of the following structure define the exported methods of the UnQLite pager
   746   * to the underlying Key/Value storage engine.
   747   */
   748  typedef struct unqlite_kv_io unqlite_kv_io;
   749  struct unqlite_kv_io
   750  {
   751  	unqlite_kv_handle  pHandle;     /* UnQLite handle passed as the first parameter to the
   752  									 * method defined below.
   753  									 */
   754  	unqlite_kv_methods *pMethods;   /* Underlying storage engine */
   755  	/* Pager methods */
   756  	int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
   757  	int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
   758  	int (*xNew)(unqlite_kv_handle,unqlite_page **);
   759  	int (*xWrite)(unqlite_page *);
   760  	int (*xDontWrite)(unqlite_page *);
   761  	int (*xDontJournal)(unqlite_page *);
   762  	int (*xDontMkHot)(unqlite_page *);
   763  	int (*xPageRef)(unqlite_page *);
   764  	int (*xPageUnref)(unqlite_page *);
   765  	int (*xPageSize)(unqlite_kv_handle);
   766  	int (*xReadOnly)(unqlite_kv_handle);
   767  	unsigned char * (*xTmpPage)(unqlite_kv_handle);
   768  	void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); 
   769  	void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
   770  	void (*xErr)(unqlite_kv_handle,const char *);
   771  };
   772  /*
   773   * Key/Value Storage Engine Cursor Object
   774   *
   775   * An instance of a subclass of the following object defines a cursor
   776   * used to scan through a key-value storage engine.
   777   */
   778  typedef struct unqlite_kv_cursor unqlite_kv_cursor;
   779  struct unqlite_kv_cursor
   780  {
   781    unqlite_kv_engine *pStore; /* Must be first */
   782    /* Subclasses will typically add additional fields */
   783  };
   784  /*
   785   * Possible seek positions.
   786   */
   787  #define UNQLITE_CURSOR_MATCH_EXACT  1
   788  #define UNQLITE_CURSOR_MATCH_LE     2
   789  #define UNQLITE_CURSOR_MATCH_GE     3
   790  /*
   791   * Key/Value Storage Engine.
   792   *
   793   * A Key-Value storage engine is defined by an instance of the following
   794   * object.
   795   * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
   796   * The storage engine works with key/value pairs where both the key
   797   * and the value are byte arrays of arbitrary length and with no restrictions on content.
   798   * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
   799   * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
   800   * hash-table or Red-black tree storage engine is used for in-memory databases.
   801   * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). 
   802   * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
   803   * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
   804   */
   805  struct unqlite_kv_engine
   806  {
   807    const unqlite_kv_io *pIo; /* IO methods: MUST be first */
   808     /* Subclasses will typically add additional fields */
   809  };
   810  /*
   811   * Key/Value Storage Engine Virtual Method Table.
   812   *
   813   * Key/Value storage engine methods is defined by an instance of the following
   814   * object.
   815   * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
   816   * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
   817   */
   818  struct unqlite_kv_methods
   819  {
   820    const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
   821    int szKv;          /* 'unqlite_kv_engine' subclass size */
   822    int szCursor;      /* 'unqlite_kv_cursor' subclass size */
   823    int iVersion;      /* Structure version, currently 1 */
   824    /* Storage engine methods */
   825    int (*xInit)(unqlite_kv_engine *,int iPageSize);
   826    void (*xRelease)(unqlite_kv_engine *);
   827    int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
   828    int (*xOpen)(unqlite_kv_engine *,pgno);
   829    int (*xReplace)(
   830  	  unqlite_kv_engine *,
   831  	  const void *pKey,int nKeyLen,
   832  	  const void *pData,unqlite_int64 nDataLen
   833  	  ); 
   834      int (*xAppend)(
   835  	  unqlite_kv_engine *,
   836  	  const void *pKey,int nKeyLen,
   837  	  const void *pData,unqlite_int64 nDataLen
   838  	  );
   839    void (*xCursorInit)(unqlite_kv_cursor *);
   840    int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
   841    int (*xFirst)(unqlite_kv_cursor *);
   842    int (*xLast)(unqlite_kv_cursor *);
   843    int (*xValid)(unqlite_kv_cursor *);
   844    int (*xNext)(unqlite_kv_cursor *);
   845    int (*xPrev)(unqlite_kv_cursor *);
   846    int (*xDelete)(unqlite_kv_cursor *);
   847    int (*xKeyLength)(unqlite_kv_cursor *,int *);
   848    int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
   849    int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
   850    int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
   851    void (*xReset)(unqlite_kv_cursor *);
   852    void (*xCursorRelease)(unqlite_kv_cursor *);
   853  };
   854  /*
   855   * UnQLite journal file suffix.
   856   */
   857  #ifndef UNQLITE_JOURNAL_FILE_SUFFIX
   858  #define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
   859  #endif
   860  /*
   861   * Call Context - Error Message Serverity Level.
   862   *
   863   * The following constans are the allowed severity level that can
   864   * passed as the second argument to the [unqlite_context_throw_error()] or
   865   * [unqlite_context_throw_error_format()] interfaces.
   866   * Refer to the official documentation for additional information.
   867   */
   868  #define UNQLITE_CTX_ERR       1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
   869  #define UNQLITE_CTX_WARNING   2 /* Call context Warning */
   870  #define UNQLITE_CTX_NOTICE    3 /* Call context Notice */
   871  /* 
   872   * C-API-REF: Please refer to the official documentation for interfaces
   873   * purpose and expected parameters. 
   874   */ 
   875  
   876  /* Database Engine Handle */
   877  UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
   878  UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
   879  UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
   880  
   881  /* Key/Value (KV) Store Interfaces */
   882  UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
   883  UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
   884  UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
   885  UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
   886  UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
   887  UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
   888  	                    int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
   889  UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
   890  UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
   891  
   892  /* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
   893  UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
   894  UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
   895  UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
   896  UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
   897  UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
   898  UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
   899  UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
   900  UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
   901  
   902  /*  Cursor Iterator Interfaces */
   903  UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
   904  UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
   905  UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
   906  UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
   907  UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
   908  UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
   909  UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
   910  UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
   911  UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
   912  UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
   913  UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
   914  UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
   915  UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
   916  UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
   917  
   918  /* Manual Transaction Manager */
   919  UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
   920  UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
   921  UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
   922  
   923  /* Utility interfaces */
   924  UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
   925  UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
   926  UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
   927  UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
   928  
   929  /* In-process extending interfaces */
   930  UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
   931  UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
   932  UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
   933  UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
   934  
   935  /* On Demand Object allocation interfaces */
   936  UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
   937  UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
   938  UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
   939  UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
   940  UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
   941  UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
   942  
   943  /* Dynamically Typed Value Object Management Interfaces */
   944  UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
   945  UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
   946  UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
   947  UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
   948  UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
   949  UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
   950  UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
   951  UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
   952  UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
   953  UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
   954  
   955  /* Foreign Function Parameter Values */
   956  UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
   957  UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
   958  UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
   959  UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
   960  UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
   961  UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
   962  UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
   963  
   964  /* Setting The Result Of A Foreign Function */
   965  UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
   966  UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
   967  UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
   968  UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
   969  UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
   970  UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
   971  UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
   972  UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
   973  UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
   974  
   975  /* Dynamically Typed Value Object Query Interfaces */
   976  UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
   977  UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
   978  UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
   979  UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
   980  UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
   981  UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
   982  UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
   983  UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
   984  UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
   985  UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
   986  UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
   987  UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
   988  
   989  /* JSON Array/Object Management Interfaces */
   990  UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
   991  UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
   992  UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
   993  UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
   994  UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
   995  
   996  /* Call Context Handling Interfaces */
   997  UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
   998  UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
   999  UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
  1000  UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
  1001  UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
  1002  UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
  1003  UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
  1004  UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
  1005  UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
  1006  UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
  1007  UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
  1008  
  1009  /* Call Context Memory Management Interfaces */
  1010  UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
  1011  UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
  1012  UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
  1013  
  1014  /* Global Library Management Interfaces */
  1015  UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
  1016  UNQLITE_APIEXPORT int unqlite_lib_init(void);
  1017  UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
  1018  UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
  1019  UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
  1020  UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
  1021  UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
  1022  UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
  1023  #ifdef __cplusplus
  1024  }
  1025  #endif /* __cplusplus */
  1026  #endif /* _UNQLITE_H_ */
  1027  /*
  1028   * ----------------------------------------------------------
  1029   * File: jx9.h
  1030   * MD5: d23a1e182f596794001533e1d6aa16a0
  1031   * ----------------------------------------------------------
  1032   */
  1033  /* This file was automatically generated.  Do not edit (except for compile time directive)! */ 
  1034  #ifndef _JX9H_
  1035  #define _JX9H_
  1036  /*
  1037   * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  1038   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  1039   * Version 1.7.2
  1040   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  1041   * please contact Symisc Systems via:
  1042   *       legal@symisc.net
  1043   *       licensing@symisc.net
  1044   *       contact@symisc.net
  1045   * or visit:
  1046   *      http://jx9.symisc.net/
  1047   */
  1048  /*
  1049   * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
  1050   *
  1051   * Redistribution and use in source and binary forms, with or without
  1052   * modification, are permitted provided that the following conditions
  1053   * are met:
  1054   * 1. Redistributions of source code must retain the above copyright
  1055   *    notice, this list of conditions and the following disclaimer.
  1056   * 2. Redistributions in binary form must reproduce the above copyright
  1057   *    notice, this list of conditions and the following disclaimer in the
  1058   *    documentation and/or other materials provided with the distribution.
  1059   * 3. Redistributions in any form must be accompanied by information on
  1060   *    how to obtain complete source code for the JX9 engine and any 
  1061   *    accompanying software that uses the JX9 engine software.
  1062   *    The source code must either be included in the distribution
  1063   *    or be available for no more than the cost of distribution plus
  1064   *    a nominal fee, and must be freely redistributable under reasonable
  1065   *    conditions. For an executable file, complete source code means
  1066   *    the source code for all modules it contains.It does not include
  1067   *    source code for modules or files that typically accompany the major
  1068   *    components of the operating system on which the executable file runs.
  1069   *
  1070   * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
  1071   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  1072   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
  1073   * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
  1074   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  1075   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  1076   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  1077   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  1078   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  1079   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  1080   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1081   */
  1082   /* $SymiscID: jx9.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable <chm@symisc.net> $ */
  1083  #include "unqlite.h"
  1084  /*
  1085   * Compile time engine version, signature, identification in the symisc source tree
  1086   * and copyright notice.
  1087   * Each macro have an equivalent C interface associated with it that provide the same
  1088   * information but are associated with the library instead of the header file.
  1089   * Refer to [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and
  1090   * [jx9_lib_copyright()] for more information.
  1091   */
  1092  /*
  1093   * The JX9_VERSION C preprocessor macroevaluates to a string literal
  1094   * that is the jx9 version in the format "X.Y.Z" where X is the major
  1095   * version number and Y is the minor version number and Z is the release
  1096   * number.
  1097   */
  1098  #define JX9_VERSION "1.7.2"
  1099  /*
  1100   * The JX9_VERSION_NUMBER C preprocessor macro resolves to an integer
  1101   * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
  1102   * numbers used in [JX9_VERSION].
  1103   */
  1104  #define JX9_VERSION_NUMBER 1007002
  1105  /*
  1106   * The JX9_SIG C preprocessor macro evaluates to a string
  1107   * literal which is the public signature of the jx9 engine.
  1108   * This signature could be included for example in a host-application
  1109   * generated Server MIME header as follows:
  1110   *   Server: YourWebServer/x.x Jx9/x.x.x \r\n
  1111   */
  1112  #define JX9_SIG "Jx9/1.7.2"
  1113  /*
  1114   * JX9 identification in the Symisc source tree:
  1115   * Each particular check-in of a particular software released
  1116   * by symisc systems have an unique identifier associated with it.
  1117   * This macro hold the one associated with jx9.
  1118   */
  1119  #define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7"
  1120  /*
  1121   * Copyright notice.
  1122   * If you have any questions about the licensing situation, please
  1123   * visit http://jx9.symisc.net/licensing.html
  1124   * or contact Symisc Systems via:
  1125   *   legal@symisc.net
  1126   *   licensing@symisc.net
  1127   *   contact@symisc.net
  1128   */
  1129  #define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/"
  1130  
  1131  /* Forward declaration to public objects */
  1132  typedef struct jx9_io_stream jx9_io_stream;
  1133  typedef struct jx9_context jx9_context;
  1134  typedef struct jx9_value jx9_value;
  1135  typedef struct jx9_vfs jx9_vfs;
  1136  typedef struct jx9_vm jx9_vm;
  1137  typedef struct jx9 jx9;
  1138  
  1139  #include "unqlite.h"
  1140  
  1141  #if !defined( UNQLITE_ENABLE_JX9_HASH_FUNC )
  1142  #define JX9_DISABLE_HASH_FUNC
  1143  #endif /* UNQLITE_ENABLE_JX9_HASH_FUNC */
  1144  #ifdef UNQLITE_ENABLE_THREADS
  1145  #define JX9_ENABLE_THREADS
  1146  #endif /* UNQLITE_ENABLE_THREADS */
  1147  /* Standard JX9 return values */
  1148  #define JX9_OK      SXRET_OK      /* Successful result */
  1149  /* beginning-of-error-codes */
  1150  #define JX9_NOMEM   UNQLITE_NOMEM     /* Out of memory */
  1151  #define JX9_ABORT   UNQLITE_ABORT   /* Foreign Function request operation abort/Another thread have released this instance */
  1152  #define JX9_IO_ERR  UNQLITE_IOERR      /* IO error */
  1153  #define JX9_CORRUPT UNQLITE_CORRUPT /* Corrupt pointer/Unknown configuration option */
  1154  #define JX9_LOOKED  UNQLITE_LOCKED  /* Forbidden Operation */ 
  1155  #define JX9_COMPILE_ERR UNQLITE_COMPILE_ERR /* Compilation error */
  1156  #define JX9_VM_ERR      UNQLITE_VM_ERR      /* Virtual machine error */
  1157  /* end-of-error-codes */
  1158  /*
  1159   * If compiling for a processor that lacks floating point
  1160   * support, substitute integer for floating-point.
  1161   */
  1162  #ifdef JX9_OMIT_FLOATING_POINT
  1163  typedef sxi64 jx9_real;
  1164  #else
  1165  typedef double jx9_real;
  1166  #endif
  1167  typedef sxi64 jx9_int64;
  1168  /*
  1169   * Engine Configuration Commands.
  1170   *
  1171   * The following set of constants are the available configuration verbs that can
  1172   * be used by the host-application to configure the JX9 engine.
  1173   * These constants must be passed as the second argument to the [jx9_config()] 
  1174   * interface.
  1175   * Each options require a variable number of arguments.
  1176   * The [jx9_config()] interface will return JX9_OK on success, any other
  1177   * return value indicates failure.
  1178   * For a full discussion on the configuration verbs and their expected 
  1179   * parameters, please refer to this page:
  1180   *      http://jx9.symisc.net/c_api_func.html#jx9_config
  1181   */
  1182  #define JX9_CONFIG_ERR_ABORT     1  /* RESERVED FOR FUTURE USE */
  1183  #define JX9_CONFIG_ERR_LOG       2  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
  1184  /*
  1185   * Virtual Machine Configuration Commands.
  1186   *
  1187   * The following set of constants are the available configuration verbs that can
  1188   * be used by the host-application to configure the JX9 Virtual machine.
  1189   * These constants must be passed as the second argument to the [jx9_vm_config()] 
  1190   * interface.
  1191   * Each options require a variable number of arguments.
  1192   * The [jx9_vm_config()] interface will return JX9_OK on success, any other return
  1193   * value indicates failure.
  1194   * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install
  1195   * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register
  1196   * a HTTP request and JX9_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
  1197   * For a full discussion on the configuration verbs and their expected parameters, please
  1198   * refer to this page:
  1199   *      http://jx9.symisc.net/c_api_func.html#jx9_vm_config
  1200   */
  1201  #define JX9_VM_CONFIG_OUTPUT           UNQLITE_VM_CONFIG_OUTPUT  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
  1202  #define JX9_VM_CONFIG_IMPORT_PATH      UNQLITE_VM_CONFIG_IMPORT_PATH  /* ONE ARGUMENT: const char *zIncludePath */
  1203  #define JX9_VM_CONFIG_ERR_REPORT       UNQLITE_VM_CONFIG_ERR_REPORT  /* NO ARGUMENTS: Report all run-time errors in the VM output */
  1204  #define JX9_VM_CONFIG_RECURSION_DEPTH  UNQLITE_VM_CONFIG_RECURSION_DEPTH  /* ONE ARGUMENT: int nMaxDepth */
  1205  #define JX9_VM_OUTPUT_LENGTH           UNQLITE_VM_OUTPUT_LENGTH  /* ONE ARGUMENT: unsigned int *pLength */
  1206  #define JX9_VM_CONFIG_CREATE_VAR       UNQLITE_VM_CONFIG_CREATE_VAR  /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
  1207  #define JX9_VM_CONFIG_HTTP_REQUEST     UNQLITE_VM_CONFIG_HTTP_REQUEST  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
  1208  #define JX9_VM_CONFIG_SERVER_ATTR      UNQLITE_VM_CONFIG_SERVER_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
  1209  #define JX9_VM_CONFIG_ENV_ATTR         UNQLITE_VM_CONFIG_ENV_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
  1210  #define JX9_VM_CONFIG_EXEC_VALUE       UNQLITE_VM_CONFIG_EXEC_VALUE  /* ONE ARGUMENT: jx9_value **ppValue */
  1211  #define JX9_VM_CONFIG_IO_STREAM        UNQLITE_VM_CONFIG_IO_STREAM  /* ONE ARGUMENT: const jx9_io_stream *pStream */
  1212  #define JX9_VM_CONFIG_ARGV_ENTRY       UNQLITE_VM_CONFIG_ARGV_ENTRY  /* ONE ARGUMENT: const char *zValue */
  1213  #define JX9_VM_CONFIG_EXTRACT_OUTPUT   UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
  1214  /*
  1215   * Global Library Configuration Commands.
  1216   *
  1217   * The following set of constants are the available configuration verbs that can
  1218   * be used by the host-application to configure the whole library.
  1219   * These constants must be passed as the first argument to the [jx9_lib_config()] 
  1220   * interface.
  1221   * Each options require a variable number of arguments.
  1222   * The [jx9_lib_config()] interface will return JX9_OK on success, any other return
  1223   * value indicates failure.
  1224   * Notes:
  1225   * The default configuration is recommended for most applications and so the call to
  1226   * [jx9_lib_config()] is usually not necessary. It is provided to support rare 
  1227   * applications with unusual needs. 
  1228   * The [jx9_lib_config()] interface is not threadsafe. The application must insure that
  1229   * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()]
  1230   * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library
  1231   * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown
  1232   * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()]
  1233   * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED.
  1234   * For a full discussion on the configuration verbs and their expected parameters, please
  1235   * refer to this page:
  1236   *      http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces
  1237   */
  1238  #define JX9_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ 
  1239  #define JX9_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
  1240  #define JX9_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ 
  1241  #define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */ 
  1242  #define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */ 
  1243  #define JX9_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const jx9_vfs *pVfs */
  1244  /*
  1245   * Call Context - Error Message Serverity Level.
  1246   */
  1247  #define JX9_CTX_ERR      UNQLITE_CTX_ERR      /* Call context error such as unexpected number of arguments, invalid types and so on. */
  1248  #define JX9_CTX_WARNING  UNQLITE_CTX_WARNING  /* Call context Warning */
  1249  #define JX9_CTX_NOTICE   UNQLITE_CTX_NOTICE   /* Call context Notice */
  1250  /* Current VFS structure version*/
  1251  #define JX9_VFS_VERSION 2 
  1252  /* 
  1253   * JX9 Virtual File System (VFS).
  1254   *
  1255   * An instance of the jx9_vfs object defines the interface between the JX9 core
  1256   * and the underlying operating system. The "vfs" in the name of the object stands
  1257   * for "virtual file system". The vfs is used to implement JX9 system functions
  1258   * such as mkdir(), chdir(), stat(), get_user_name() and many more.
  1259   * The value of the iVersion field is initially 2 but may be larger in future versions
  1260   * of JX9.
  1261   * Additional fields may be appended to this object when the iVersion value is increased.
  1262   * Only a single vfs can be registered within the JX9 core. Vfs registration is done
  1263   * using the jx9_lib_config() interface with a configuration verb set to JX9_LIB_CONFIG_VFS.
  1264   * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to
  1265   * worry about registering and installing a vfs since JX9 come with a built-in vfs for these
  1266   * platforms which implement most the methods defined below.
  1267   * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must
  1268   * register their own vfs in order to be able to use and call JX9 system functions.
  1269   * Also note that the jx9_compile_file() interface depend on the xMmap() method of the underlying
  1270   * vfs which mean that this method must be available (Always the case using the built-in VFS)
  1271   * in order to use this interface.
  1272   * Developers wishing to implement their own vfs an contact symisc systems to obtain
  1273   * the JX9 VFS C/C++ Specification manual.
  1274   */
  1275  struct jx9_vfs
  1276  {
  1277  	const char *zName;  /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */
  1278  	int iVersion;       /* Current VFS structure version [default 2] */
  1279  	/* Directory functions */
  1280  	int (*xChdir)(const char *);                     /* Change directory */
  1281  	int (*xChroot)(const char *);                    /* Change the root directory */
  1282  	int (*xGetcwd)(jx9_context *);                   /* Get the current working directory */
  1283  	int (*xMkdir)(const char *, int, int);             /* Make directory */
  1284  	int (*xRmdir)(const char *);                     /* Remove directory */
  1285  	int (*xIsdir)(const char *);                     /* Tells whether the filename is a directory */
  1286  	int (*xRename)(const char *, const char *);       /* Renames a file or directory */
  1287  	int (*xRealpath)(const char *, jx9_context *);    /* Return canonicalized absolute pathname*/
  1288  	/* Systems functions */
  1289  	int (*xSleep)(unsigned int);                     /* Delay execution in microseconds */
  1290  	int (*xUnlink)(const char *);                    /* Deletes a file */
  1291  	int (*xFileExists)(const char *);                /* Checks whether a file or directory exists */
  1292  	int (*xChmod)(const char *, int);                 /* Changes file mode */
  1293  	int (*xChown)(const char *, const char *);        /* Changes file owner */
  1294  	int (*xChgrp)(const char *, const char *);        /* Changes file group */
  1295  	jx9_int64 (*xFreeSpace)(const char *);           /* Available space on filesystem or disk partition */
  1296  	jx9_int64 (*xTotalSpace)(const char *);          /* Total space on filesystem or disk partition */
  1297  	jx9_int64 (*xFileSize)(const char *);            /* Gets file size */
  1298  	jx9_int64 (*xFileAtime)(const char *);           /* Gets last access time of file */
  1299  	jx9_int64 (*xFileMtime)(const char *);           /* Gets file modification time */
  1300  	jx9_int64 (*xFileCtime)(const char *);           /* Gets inode change time of file */
  1301  	int (*xStat)(const char *, jx9_value *, jx9_value *);   /* Gives information about a file */
  1302  	int (*xlStat)(const char *, jx9_value *, jx9_value *);  /* Gives information about a file */
  1303  	int (*xIsfile)(const char *);                    /* Tells whether the filename is a regular file */
  1304  	int (*xIslink)(const char *);                    /* Tells whether the filename is a symbolic link */
  1305  	int (*xReadable)(const char *);                  /* Tells whether a file exists and is readable */
  1306  	int (*xWritable)(const char *);                  /* Tells whether the filename is writable */
  1307  	int (*xExecutable)(const char *);                /* Tells whether the filename is executable */
  1308  	int (*xFiletype)(const char *, jx9_context *);    /* Gets file type [i.e: fifo, dir, file..] */
  1309  	int (*xGetenv)(const char *, jx9_context *);      /* Gets the value of an environment variable */
  1310  	int (*xSetenv)(const char *, const char *);       /* Sets the value of an environment variable */
  1311  	int (*xTouch)(const char *, jx9_int64, jx9_int64); /* Sets access and modification time of file */
  1312  	int (*xMmap)(const char *, void **, jx9_int64 *);  /* Read-only memory map of the whole file */
  1313  	void (*xUnmap)(void *, jx9_int64);                /* Unmap a memory view */
  1314  	int (*xLink)(const char *, const char *, int);     /* Create hard or symbolic link */
  1315  	int (*xUmask)(int);                              /* Change the current umask */
  1316  	void (*xTempDir)(jx9_context *);                 /* Get path of the temporary directory */
  1317  	unsigned int (*xProcessId)(void);                /* Get running process ID */
  1318  	int (*xUid)(void);                               /* user ID of the process */
  1319  	int (*xGid)(void);                               /* group ID of the process */
  1320  	void (*xUsername)(jx9_context *);                /* Running username */
  1321  	int (*xExec)(const char *, jx9_context *);        /* Execute an external program */
  1322  };
  1323  /* Current JX9 IO stream structure version. */
  1324  #define JX9_IO_STREAM_VERSION 1 
  1325  /* 
  1326   * Possible open mode flags that can be passed to the xOpen() routine
  1327   * of the underlying IO stream device .
  1328   * Refer to the JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html)
  1329   * for additional information.
  1330   */
  1331  #define JX9_IO_OPEN_RDONLY   0x001  /* Read-only open */
  1332  #define JX9_IO_OPEN_WRONLY   0x002  /* Write-only open */
  1333  #define JX9_IO_OPEN_RDWR     0x004  /* Read-write open. */
  1334  #define JX9_IO_OPEN_CREATE   0x008  /* If the file does not exist it will be created */
  1335  #define JX9_IO_OPEN_TRUNC    0x010  /* Truncate the file to zero length */
  1336  #define JX9_IO_OPEN_APPEND   0x020  /* Append mode.The file offset is positioned at the end of the file */
  1337  #define JX9_IO_OPEN_EXCL     0x040  /* Ensure that this call creates the file, the file must not exist before */
  1338  #define JX9_IO_OPEN_BINARY   0x080  /* Simple hint: Data is binary */
  1339  #define JX9_IO_OPEN_TEMP     0x100  /* Simple hint: Temporary file */
  1340  #define JX9_IO_OPEN_TEXT     0x200  /* Simple hint: Data is textual */
  1341  /*
  1342   * JX9 IO Stream Device.
  1343   *
  1344   * An instance of the jx9_io_stream object defines the interface between the JX9 core
  1345   * and the underlying stream device.
  1346   * A stream is a smart mechanism for generalizing file, network, data compression
  1347   * and other IO operations which share a common set of functions using an abstracted
  1348   * unified interface.
  1349   * A stream device is additional code which tells the stream how to handle specific
  1350   * protocols/encodings. For example, the http device knows how to translate a URL
  1351   * into an HTTP/1.1 request for a file on a remote server.
  1352   * JX9 come with two built-in IO streams device:
  1353   * The file:// stream which perform very efficient disk IO and the jx9:// stream
  1354   * which is a special stream that allow access various I/O streams (See the JX9 official
  1355   * documentation for more information on this stream).
  1356   * A stream is referenced as: scheme://target 
  1357   * scheme(string) - The name of the wrapper to be used. Examples include: file, http, https, ftp, 
  1358   * ftps, compress.zlib, compress.bz2, and jx9. If no wrapper is specified, the function default
  1359   * is used (typically file://). 
  1360   * target - Depends on the device used. For filesystem related streams this is typically a path
  1361   * and filename of the desired file.For network related streams this is typically a hostname, often
  1362   * with a path appended. 
  1363   * IO stream devices are registered using a call to jx9_vm_config() with a configuration verb
  1364   * set to JX9_VM_CONFIG_IO_STREAM.
  1365   * Currently the JX9 development team is working on the implementation of the http:// and ftp://
  1366   * IO stream protocols. These devices will be available in the next major release of the JX9 engine.
  1367   * Developers wishing to implement their own IO stream devices must understand and follow
  1368   * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html).
  1369   */
  1370  struct jx9_io_stream
  1371  {
  1372  	const char *zName;                                     /* Underlying stream name [i.e: file/http/zip/jx9, ..] */
  1373  	int iVersion;                                          /* IO stream structure version [default 1]*/
  1374  	int  (*xOpen)(const char *, int, jx9_value *, void **);   /* Open handle*/
  1375  	int  (*xOpenDir)(const char *, jx9_value *, void **);    /* Open directory handle */
  1376  	void (*xClose)(void *);                                /* Close file handle */
  1377  	void (*xCloseDir)(void *);                             /* Close directory handle */
  1378  	jx9_int64 (*xRead)(void *, void *, jx9_int64);           /* Read from the open stream */         
  1379  	int (*xReadDir)(void *, jx9_context *);                 /* Read entry from directory handle */
  1380  	jx9_int64 (*xWrite)(void *, const void *, jx9_int64);    /* Write to the open stream */
  1381  	int (*xSeek)(void *, jx9_int64, int);                    /* Seek on the open stream */
  1382  	int (*xLock)(void *, int);                              /* Lock/Unlock the open stream */
  1383  	void (*xRewindDir)(void *);                            /* Rewind directory handle */
  1384  	jx9_int64 (*xTell)(void *);                            /* Current position of the stream  read/write pointer */
  1385  	int (*xTrunc)(void *, jx9_int64);                       /* Truncates the open stream to a given length */
  1386  	int (*xSync)(void *);                                  /* Flush open stream data */
  1387  	int (*xStat)(void *, jx9_value *, jx9_value *);          /* Stat an open stream handle */
  1388  };
  1389  /* 
  1390   * C-API-REF: Please refer to the official documentation for interfaces
  1391   * purpose and expected parameters. 
  1392   */ 
  1393  /* Engine Handling Interfaces */
  1394  JX9_PRIVATE int jx9_init(jx9 **ppEngine);
  1395  /*JX9_PRIVATE int jx9_config(jx9 *pEngine, int nConfigOp, ...);*/
  1396  JX9_PRIVATE int jx9_release(jx9 *pEngine);
  1397  /* Compile Interfaces */
  1398  JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
  1399  JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
  1400  /* Virtual Machine Handling Interfaces */
  1401  JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
  1402  /*JX9_PRIVATE int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);*/
  1403  /*JX9_PRIVATE jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);*/
  1404  /*JX9_PRIVATE int jx9_vm_reset(jx9_vm *pVm);*/
  1405  JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm);
  1406  /*JX9_PRIVATE int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);*/
  1407  /* In-process Extending Interfaces */
  1408  JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
  1409  /*JX9_PRIVATE int jx9_delete_function(jx9_vm *pVm, const char *zName);*/
  1410  JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
  1411  /*JX9_PRIVATE int jx9_delete_constant(jx9_vm *pVm, const char *zName);*/
  1412  /* Foreign Function Parameter Values */
  1413  JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue);
  1414  JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue);
  1415  JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue);
  1416  JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue);
  1417  JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
  1418  JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue);
  1419  JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
  1420  /* Setting The Result Of A Foreign Function */
  1421  JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue);
  1422  JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
  1423  JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool);
  1424  JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value);
  1425  JX9_PRIVATE int jx9_result_null(jx9_context *pCtx);
  1426  JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
  1427  JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
  1428  JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
  1429  JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData);
  1430  /* Call Context Handling Interfaces */
  1431  JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
  1432  /*JX9_PRIVATE int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);*/
  1433  JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
  1434  JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
  1435  JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx);
  1436  JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
  1437  JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx);
  1438  JX9_PRIVATE int    jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
  1439  JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx);
  1440  JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx);
  1441  JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
  1442  JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx);
  1443  /* Call Context Memory Management Interfaces */
  1444  JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
  1445  JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
  1446  JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
  1447  /* On Demand Dynamically Typed Value Object allocation interfaces */
  1448  JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm);
  1449  JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm);
  1450  JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
  1451  JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
  1452  JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx);
  1453  JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
  1454  /* Dynamically Typed Value Object Management Interfaces */
  1455  JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue);
  1456  JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
  1457  JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool);
  1458  JX9_PRIVATE int jx9_value_null(jx9_value *pVal);
  1459  JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value);
  1460  JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
  1461  JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
  1462  JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal);
  1463  JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData);
  1464  JX9_PRIVATE int jx9_value_release(jx9_value *pVal);
  1465  /* JSON Array/Object Management Interfaces */
  1466  JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
  1467  JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
  1468  JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
  1469  JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
  1470  JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray);
  1471  /* Dynamically Typed Value Object Query Interfaces */
  1472  JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal);
  1473  JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal);
  1474  JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal);
  1475  JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal);
  1476  JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal);
  1477  JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal);
  1478  JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal);
  1479  JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal);
  1480  JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal);
  1481  JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal);
  1482  JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal);
  1483  JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal);
  1484  /* Global Library Management Interfaces */
  1485  /*JX9_PRIVATE int jx9_lib_init(void);*/
  1486  JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...);
  1487  JX9_PRIVATE int jx9_lib_shutdown(void);
  1488  /*JX9_PRIVATE int jx9_lib_is_threadsafe(void);*/
  1489  /*JX9_PRIVATE const char * jx9_lib_version(void);*/
  1490  JX9_PRIVATE const char * jx9_lib_signature(void);
  1491  /*JX9_PRIVATE const char * jx9_lib_ident(void);*/
  1492  /*JX9_PRIVATE const char * jx9_lib_copyright(void);*/
  1493  
  1494  #endif /* _JX9H_ */
  1495  
  1496  /*
  1497   * ----------------------------------------------------------
  1498   * File: jx9Int.h
  1499   * MD5: fb8dffc8ba1425a139091aa145067e16
  1500   * ----------------------------------------------------------
  1501   */
  1502  /*
  1503   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  1504   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  1505   * Version 1.7.2
  1506   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  1507   * please contact Symisc Systems via:
  1508   *       legal@symisc.net
  1509   *       licensing@symisc.net
  1510   *       contact@symisc.net
  1511   * or visit:
  1512   *      http://jx9.symisc.net/
  1513   */
  1514   /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel <chm@symisc.net> $ */
  1515  #ifndef __JX9INT_H__
  1516  #define __JX9INT_H__
  1517  /* Internal interface definitions for JX9. */
  1518  #ifdef JX9_AMALGAMATION
  1519  #ifndef JX9_PRIVATE
  1520  /* Marker for routines not intended for external use */
  1521  #define JX9_PRIVATE static
  1522  #endif /* JX9_PRIVATE */
  1523  #else
  1524  #define JX9_PRIVATE
  1525  #include "jx9.h"
  1526  #endif 
  1527  #ifndef JX9_PI
  1528  /* Value of PI */
  1529  #define JX9_PI 3.1415926535898
  1530  #endif
  1531  /*
  1532   * Constants for the largest and smallest possible 64-bit signed integers.
  1533   * These macros are designed to work correctly on both 32-bit and 64-bit
  1534   * compilers.
  1535   */
  1536  #ifndef LARGEST_INT64
  1537  #define LARGEST_INT64  (0xffffffff|(((sxi64)0x7fffffff)<<32))
  1538  #endif
  1539  #ifndef SMALLEST_INT64
  1540  #define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64)
  1541  #endif
  1542  /* Forward declaration of private structures */
  1543  typedef struct jx9_foreach_info   jx9_foreach_info;
  1544  typedef struct jx9_foreach_step   jx9_foreach_step;
  1545  typedef struct jx9_hashmap_node   jx9_hashmap_node;
  1546  typedef struct jx9_hashmap        jx9_hashmap;
  1547  /* Symisc Standard types */
  1548  #if !defined(SYMISC_STD_TYPES)
  1549  #define SYMISC_STD_TYPES
  1550  #ifdef __WINNT__
  1551  /* Disable nuisance warnings on Borland compilers */
  1552  #if defined(__BORLANDC__)
  1553  #pragma warn -rch /* unreachable code */
  1554  #pragma warn -ccc /* Condition is always true or false */
  1555  #pragma warn -aus /* Assigned value is never used */
  1556  #pragma warn -csu /* Comparing signed and unsigned */
  1557  #pragma warn -spa /* Suspicious pointer arithmetic */
  1558  #endif
  1559  #endif
  1560  typedef signed char        sxi8; /* signed char */
  1561  typedef unsigned char      sxu8; /* unsigned char */
  1562  typedef signed short int   sxi16; /* 16 bits(2 bytes) signed integer */
  1563  typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */
  1564  typedef int                sxi32; /* 32 bits(4 bytes) integer */
  1565  typedef unsigned int       sxu32; /* 32 bits(4 bytes) unsigned integer */
  1566  typedef long               sxptr;
  1567  typedef unsigned long      sxuptr;
  1568  typedef long               sxlong;
  1569  typedef unsigned long      sxulong;
  1570  typedef sxi32              sxofft;
  1571  typedef sxi64              sxofft64;
  1572  typedef long double	       sxlongreal;
  1573  typedef double             sxreal;
  1574  #define SXI8_HIGH       0x7F
  1575  #define SXU8_HIGH       0xFF
  1576  #define SXI16_HIGH      0x7FFF
  1577  #define SXU16_HIGH      0xFFFF
  1578  #define SXI32_HIGH      0x7FFFFFFF
  1579  #define SXU32_HIGH      0xFFFFFFFF
  1580  #define SXI64_HIGH      0x7FFFFFFFFFFFFFFF
  1581  #define SXU64_HIGH      0xFFFFFFFFFFFFFFFF 
  1582  #if !defined(TRUE)
  1583  #define TRUE 1
  1584  #endif
  1585  #if !defined(FALSE)
  1586  #define FALSE 0
  1587  #endif
  1588  /*
  1589   * The following macros are used to cast pointers to integers and
  1590   * integers to pointers.
  1591   */
  1592  #if defined(__PTRDIFF_TYPE__)  
  1593  # define SX_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
  1594  # define SX_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
  1595  #elif !defined(__GNUC__)    
  1596  # define SX_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
  1597  # define SX_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
  1598  #else                       
  1599  # define SX_INT_TO_PTR(X)  ((void*)(X))
  1600  # define SX_PTR_TO_INT(X)  ((int)(X))
  1601  #endif
  1602  #define SXMIN(a, b)  ((a < b) ? (a) : (b))
  1603  #define SXMAX(a, b)  ((a < b) ? (b) : (a))
  1604  #endif /* SYMISC_STD_TYPES */
  1605  /* Symisc Run-time API private definitions */
  1606  #if !defined(SYMISC_PRIVATE_DEFS)
  1607  #define SYMISC_PRIVATE_DEFS
  1608  
  1609  typedef sxi32 (*ProcRawStrCmp)(const SyString *, const SyString *);
  1610  #define SyStringData(RAW)	((RAW)->zString)
  1611  #define SyStringLength(RAW)	((RAW)->nByte)
  1612  #define SyStringInitFromBuf(RAW, ZBUF, NLEN){\
  1613  	(RAW)->zString 	= (const char *)ZBUF;\
  1614  	(RAW)->nByte	= (sxu32)(NLEN);\
  1615  }
  1616  #define SyStringUpdatePtr(RAW, NBYTES){\
  1617  	if( NBYTES > (RAW)->nByte ){\
  1618  		(RAW)->nByte = 0;\
  1619  	}else{\
  1620  		(RAW)->zString += NBYTES;\
  1621  		(RAW)->nByte -= NBYTES;\
  1622  	}\
  1623  }
  1624  #define SyStringDupPtr(RAW1, RAW2)\
  1625  	(RAW1)->zString = (RAW2)->zString;\
  1626  	(RAW1)->nByte = (RAW2)->nByte;
  1627  
  1628  #define SyStringTrimLeadingChar(RAW, CHAR)\
  1629  	while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\
  1630  			(RAW)->zString++;\
  1631  			(RAW)->nByte--;\
  1632  	}
  1633  #define SyStringTrimTrailingChar(RAW, CHAR)\
  1634  	while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\
  1635  		(RAW)->nByte--;\
  1636  	}
  1637  #define SyStringCmp(RAW1, RAW2, xCMP)\
  1638  	(((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte))
  1639  
  1640  #define SyStringCmp2(RAW1, RAW2, xCMP)\
  1641  	(((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte))
  1642  
  1643  #define SyStringCharCmp(RAW, CHAR) \
  1644  	(((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char)))
  1645  
  1646  #define SX_ADDR(PTR)    ((sxptr)PTR)
  1647  #define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
  1648  #define SXUNUSED(P)	(P = 0)
  1649  #define	SX_EMPTY(PTR)   (PTR == 0)
  1650  #define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 )
  1651  typedef struct SyMemBackend SyMemBackend;
  1652  typedef struct SyBlob SyBlob;
  1653  typedef struct SySet SySet;
  1654  /* Standard function signatures */
  1655  typedef sxi32 (*ProcCmp)(const void *, const void *, sxu32);
  1656  typedef sxi32 (*ProcPatternMatch)(const char *, sxu32, const char *, sxu32, sxu32 *);
  1657  typedef sxi32 (*ProcSearch)(const void *, sxu32, const void *, sxu32, ProcCmp, sxu32 *);
  1658  typedef sxu32 (*ProcHash)(const void *, sxu32);
  1659  typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32);
  1660  typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp);
  1661  #define MACRO_LIST_PUSH(Head, Item)\
  1662  	Item->pNext = Head;\
  1663  	Head = Item; 
  1664  #define MACRO_LD_PUSH(Head, Item)\
  1665  	if( Head == 0 ){\
  1666  		Head = Item;\
  1667  	}else{\
  1668  		Item->pNext = Head;\
  1669  		Head->pPrev = Item;\
  1670  		Head = Item;\
  1671  	}
  1672  #define MACRO_LD_REMOVE(Head, Item)\
  1673  	if( Head == Item ){\
  1674  		Head = Head->pNext;\
  1675  	}\
  1676  	if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\
  1677  	if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;}
  1678  /*
  1679   * A generic dynamic set.
  1680   */
  1681  struct SySet
  1682  {
  1683  	SyMemBackend *pAllocator; /* Memory backend */
  1684  	void *pBase;              /* Base pointer */	
  1685  	sxu32 nUsed;              /* Total number of used slots  */
  1686  	sxu32 nSize;              /* Total number of available slots */
  1687  	sxu32 eSize;              /* Size of a single slot */
  1688  	sxu32 nCursor;	          /* Loop cursor */	
  1689  	void *pUserData;          /* User private data associated with this container */
  1690  };
  1691  #define SySetBasePtr(S)           ((S)->pBase)
  1692  #define SySetBasePtrJump(S, OFFT)  (&((char *)(S)->pBase)[OFFT*(S)->eSize])
  1693  #define SySetUsed(S)              ((S)->nUsed)
  1694  #define SySetSize(S)              ((S)->nSize)
  1695  #define SySetElemSize(S)          ((S)->eSize) 
  1696  #define SySetCursor(S)            ((S)->nCursor)
  1697  #define SySetGetAllocator(S)      ((S)->pAllocator)
  1698  #define SySetSetUserData(S, DATA)  ((S)->pUserData = DATA)
  1699  #define SySetGetUserData(S)       ((S)->pUserData)
  1700  /*
  1701   * A variable length containers for generic data.
  1702   */
  1703  struct SyBlob
  1704  {
  1705  	SyMemBackend *pAllocator; /* Memory backend */
  1706  	void   *pBlob;	          /* Base pointer */
  1707  	sxu32  nByte;	          /* Total number of used bytes */
  1708  	sxu32  mByte;	          /* Total number of available bytes */
  1709  	sxu32  nFlags;	          /* Blob internal flags, see below */
  1710  };
  1711  #define SXBLOB_LOCKED	0x01	/* Blob is locked [i.e: Cannot auto grow] */
  1712  #define SXBLOB_STATIC	0x02	/* Not allocated from heap   */
  1713  #define SXBLOB_RDONLY   0x04    /* Read-Only data */
  1714  
  1715  #define SyBlobFreeSpace(BLOB)	 ((BLOB)->mByte - (BLOB)->nByte)
  1716  #define SyBlobLength(BLOB)	     ((BLOB)->nByte)
  1717  #define SyBlobData(BLOB)	     ((BLOB)->pBlob)
  1718  #define SyBlobCurData(BLOB)	     ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte]))
  1719  #define SyBlobDataAt(BLOB, OFFT)	 ((void *)(&((char *)(BLOB)->pBlob)[OFFT]))
  1720  #define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator)
  1721  
  1722  #define SXMEM_POOL_INCR			3
  1723  #define SXMEM_POOL_NBUCKETS		12
  1724  #define SXMEM_BACKEND_MAGIC	0xBAC3E67D
  1725  #define SXMEM_BACKEND_CORRUPT(BACKEND)	(BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC)
  1726  
  1727  #define SXMEM_BACKEND_RETRY	3
  1728  /* A memory backend subsystem is defined by an instance of the following structures */
  1729  typedef union SyMemHeader SyMemHeader;
  1730  typedef struct SyMemBlock SyMemBlock;
  1731  struct SyMemBlock
  1732  {
  1733  	SyMemBlock *pNext, *pPrev; /* Chain of allocated memory blocks */
  1734  #ifdef UNTRUST
  1735  	sxu32 nGuard;             /* magic number associated with each valid block, so we
  1736  							   * can detect misuse.
  1737  							   */
  1738  #endif
  1739  };
  1740  /*
  1741   * Header associated with each valid memory pool block.
  1742   */
  1743  union SyMemHeader
  1744  {
  1745  	SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */
  1746  	sxu32 nBucket;      /* Bucket index in aPool[] */
  1747  };
  1748  struct SyMemBackend
  1749  {
  1750  	const SyMutexMethods *pMutexMethods; /* Mutex methods */
  1751  	const SyMemMethods *pMethods;  /* Memory allocation methods */
  1752  	SyMemBlock *pBlocks;           /* List of valid memory blocks */
  1753  	sxu32 nBlock;                  /* Total number of memory blocks allocated so far */
  1754  	ProcMemError xMemError;        /* Out-of memory callback */
  1755  	void *pUserData;               /* First arg to xMemError() */
  1756  	SyMutex *pMutex;               /* Per instance mutex */
  1757  	sxu32 nMagic;                  /* Sanity check against misuse */
  1758  	SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */
  1759  };
  1760  /* Mutex types */
  1761  #define SXMUTEX_TYPE_FAST	1
  1762  #define SXMUTEX_TYPE_RECURSIVE	2
  1763  #define SXMUTEX_TYPE_STATIC_1	3
  1764  #define SXMUTEX_TYPE_STATIC_2	4
  1765  #define SXMUTEX_TYPE_STATIC_3	5
  1766  #define SXMUTEX_TYPE_STATIC_4	6
  1767  #define SXMUTEX_TYPE_STATIC_5	7
  1768  #define SXMUTEX_TYPE_STATIC_6	8
  1769  
  1770  #define SyMutexGlobalInit(METHOD){\
  1771  	if( (METHOD)->xGlobalInit ){\
  1772  	(METHOD)->xGlobalInit();\
  1773  	}\
  1774  }
  1775  #define SyMutexGlobalRelease(METHOD){\
  1776  	if( (METHOD)->xGlobalRelease ){\
  1777  	(METHOD)->xGlobalRelease();\
  1778  	}\
  1779  }
  1780  #define SyMutexNew(METHOD, TYPE)			(METHOD)->xNew(TYPE)
  1781  #define SyMutexRelease(METHOD, MUTEX){\
  1782  	if( MUTEX && (METHOD)->xRelease ){\
  1783  		(METHOD)->xRelease(MUTEX);\
  1784  	}\
  1785  }
  1786  #define SyMutexEnter(METHOD, MUTEX){\
  1787  	if( MUTEX ){\
  1788  	(METHOD)->xEnter(MUTEX);\
  1789  	}\
  1790  }
  1791  #define SyMutexTryEnter(METHOD, MUTEX){\
  1792  	if( MUTEX && (METHOD)->xTryEnter ){\
  1793  	(METHOD)->xTryEnter(MUTEX);\
  1794  	}\
  1795  }
  1796  #define SyMutexLeave(METHOD, MUTEX){\
  1797  	if( MUTEX ){\
  1798  	(METHOD)->xLeave(MUTEX);\
  1799  	}\
  1800  }
  1801  /* Comparison, byte swap, byte copy macros */
  1802  #define SX_MACRO_FAST_CMP(X1, X2, SIZE, RC){\
  1803  	register unsigned char *r1 = (unsigned char *)X1;\
  1804  	register unsigned char *r2 = (unsigned char *)X2;\
  1805  	register sxu32 LEN = SIZE;\
  1806  	for(;;){\
  1807  	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1808  	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1809  	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1810  	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1811  	}\
  1812  	RC = !LEN ? 0 : r1[0] - r2[0];\
  1813  }
  1814  #define	SX_MACRO_FAST_MEMCPY(SRC, DST, SIZ){\
  1815  	register unsigned char *xSrc = (unsigned char *)SRC;\
  1816  	register unsigned char *xDst = (unsigned char *)DST;\
  1817  	register sxu32 xLen = SIZ;\
  1818  	for(;;){\
  1819  	    if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1820  		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1821  		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1822  		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1823  	}\
  1824  }
  1825  #define SX_MACRO_BYTE_SWAP(X, Y, Z){\
  1826  	register unsigned char *s = (unsigned char *)X;\
  1827  	register unsigned char *d = (unsigned char *)Y;\
  1828  	sxu32	ZLong = Z;  \
  1829  	sxi32 c; \
  1830  	for(;;){\
  1831  	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1832  	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1833  	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1834  	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1835  	}\
  1836  }
  1837  #define SX_MSEC_PER_SEC	(1000)			/* Millisec per seconds */
  1838  #define SX_USEC_PER_SEC	(1000000)		/* Microsec per seconds */
  1839  #define SX_NSEC_PER_SEC	(1000000000)	/* Nanosec per seconds */
  1840  #endif /* SYMISC_PRIVATE_DEFS */
  1841  /* Symisc Run-time API auxiliary definitions */
  1842  #if !defined(SYMISC_PRIVATE_AUX_DEFS)
  1843  #define SYMISC_PRIVATE_AUX_DEFS
  1844  
  1845  typedef struct SyHashEntry_Pr SyHashEntry_Pr;
  1846  typedef struct SyHashEntry SyHashEntry;
  1847  typedef struct SyHash SyHash;
  1848  /*
  1849   * Each public hashtable entry is represented by an instance
  1850   * of the following structure.
  1851   */
  1852  struct SyHashEntry
  1853  {
  1854  	const void *pKey; /* Hash key */
  1855  	sxu32 nKeyLen;    /* Key length */
  1856  	void *pUserData;  /* User private data */
  1857  };
  1858  #define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData)
  1859  #define SyHashEntryGetKey(ENTRY)      ((ENTRY)->pKey)
  1860  /* Each active hashtable is identified by an instance of the following structure */
  1861  struct SyHash
  1862  {
  1863  	SyMemBackend *pAllocator;         /* Memory backend */
  1864  	ProcHash xHash;                   /* Hash function */
  1865  	ProcCmp xCmp;                     /* Comparison function */
  1866  	SyHashEntry_Pr *pList, *pCurrent;  /* Linked list of hash entries user for linear traversal */
  1867  	sxu32 nEntry;                     /* Total number of entries */
  1868  	SyHashEntry_Pr **apBucket;        /* Hash buckets */
  1869  	sxu32 nBucketSize;                /* Current bucket size */
  1870  };
  1871  #define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */
  1872  #define SXHASH_FILL_FACTOR 3
  1873  /* Hash access macro */
  1874  #define SyHashFunc(HASH)		((HASH)->xHash)
  1875  #define SyHashCmpFunc(HASH)		((HASH)->xCmp)
  1876  #define SyHashTotalEntry(HASH)	((HASH)->nEntry)
  1877  #define SyHashGetPool(HASH)		((HASH)->pAllocator)
  1878  /*
  1879   * An instance of the following structure define a single context
  1880   * for an Pseudo Random Number Generator.
  1881   *
  1882   * Nothing in this file or anywhere else in the library does any kind of
  1883   * encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
  1884   * number generator) not as an encryption device.
  1885   * This implementation is taken from the SQLite3 source tree.
  1886   */
  1887  typedef struct SyPRNGCtx SyPRNGCtx;
  1888  struct SyPRNGCtx
  1889  {
  1890      sxu8 i, j;				/* State variables */
  1891      unsigned char s[256];   /* State variables */
  1892  	sxu16 nMagic;			/* Sanity check */
  1893   };
  1894  typedef sxi32 (*ProcRandomSeed)(void *, unsigned int, void *);
  1895  /* High resolution timer.*/
  1896  typedef struct sytime sytime;
  1897  struct sytime
  1898  {
  1899  	long tm_sec;	/* seconds */
  1900  	long tm_usec;	/* microseconds */
  1901  };
  1902  /* Forward declaration */
  1903  typedef struct SyStream SyStream;
  1904  typedef struct SyToken  SyToken;
  1905  typedef struct SyLex    SyLex;
  1906  /*
  1907   * Tokenizer callback signature.
  1908   */
  1909  typedef sxi32 (*ProcTokenizer)(SyStream *, SyToken *, void *, void *);
  1910  /*
  1911   * Each token in the input is represented by an instance
  1912   * of the following structure.
  1913   */
  1914  struct SyToken
  1915  {
  1916  	SyString sData;  /* Token text and length */
  1917  	sxu32 nType;     /* Token type */
  1918  	sxu32 nLine;     /* Token line number */
  1919  	void *pUserData; /* User private data associated with this token */
  1920  };
  1921  /*
  1922   * During tokenization, information about the state of the input
  1923   * stream is held in an instance of the following structure.
  1924   */
  1925  struct SyStream
  1926  {
  1927  	const unsigned char *zInput; /* Complete text of the input */
  1928  	const unsigned char *zText; /* Current input we are processing */	
  1929  	const unsigned char *zEnd; /* End of input marker */
  1930  	sxu32  nLine; /* Total number of processed lines */
  1931  	sxu32  nIgn; /* Total number of ignored tokens */
  1932  	SySet *pSet; /* Token containers */
  1933  };
  1934  /*
  1935   * Each lexer is represented by an instance of the following structure.
  1936   */
  1937  struct SyLex
  1938  {
  1939  	SyStream sStream;         /* Input stream */
  1940  	ProcTokenizer xTokenizer; /* Tokenizer callback */
  1941  	void * pUserData;         /* Third argument to xTokenizer() */
  1942  	SySet *pTokenSet;         /* Token set */
  1943  };
  1944  #define SyLexTotalToken(LEX)    SySetTotalEntry(&(LEX)->aTokenSet)
  1945  #define SyLexTotalLines(LEX)    ((LEX)->sStream.nLine)
  1946  #define SyLexTotalIgnored(LEX)  ((LEX)->sStream.nIgn)
  1947  #define XLEX_IN_LEN(STREAM)     (sxu32)(STREAM->zEnd - STREAM->zText)
  1948  #endif /* SYMISC_PRIVATE_AUX_DEFS */
  1949  /*
  1950  ** Notes on UTF-8 (According to SQLite3 authors):
  1951  **
  1952  **   Byte-0    Byte-1    Byte-2    Byte-3    Value
  1953  **  0xxxxxxx                                 00000000 00000000 0xxxxxxx
  1954  **  110yyyyy  10xxxxxx                       00000000 00000yyy yyxxxxxx
  1955  **  1110zzzz  10yyyyyy  10xxxxxx             00000000 zzzzyyyy yyxxxxxx
  1956  **  11110uuu  10uuzzzz  10yyyyyy  10xxxxxx   000uuuuu zzzzyyyy yyxxxxxx
  1957  **
  1958  */
  1959  /*
  1960  ** Assuming zIn points to the first byte of a UTF-8 character, 
  1961  ** advance zIn to point to the first byte of the next UTF-8 character.
  1962  */
  1963  #define SX_JMP_UTF8(zIn, zEnd)\
  1964  	while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; }
  1965  #define SX_WRITE_UTF8(zOut, c) {                       \
  1966    if( c<0x00080 ){                                     \
  1967      *zOut++ = (sxu8)(c&0xFF);                          \
  1968    }else if( c<0x00800 ){                               \
  1969      *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F);              \
  1970      *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
  1971    }else if( c<0x10000 ){                               \
  1972      *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F);             \
  1973      *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F);            \
  1974      *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
  1975    }else{                                               \
  1976      *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07);           \
  1977      *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F);           \
  1978      *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F);            \
  1979      *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
  1980    }                                                    \
  1981  }
  1982  /* Rely on the standard ctype */
  1983  #include <ctype.h>
  1984  #define SyToUpper(c) toupper(c) 
  1985  #define SyToLower(c) tolower(c) 
  1986  #define SyisUpper(c) isupper(c)
  1987  #define SyisLower(c) islower(c)
  1988  #define SyisSpace(c) isspace(c)
  1989  #define SyisBlank(c) isspace(c)
  1990  #define SyisAlpha(c) isalpha(c)
  1991  #define SyisDigit(c) isdigit(c)
  1992  #define SyisHex(c)	 isxdigit(c)
  1993  #define SyisPrint(c) isprint(c)
  1994  #define SyisPunct(c) ispunct(c)
  1995  #define SyisSpec(c)	 iscntrl(c)
  1996  #define SyisCtrl(c)	 iscntrl(c)
  1997  #define SyisAscii(c) isascii(c)
  1998  #define SyisAlphaNum(c) isalnum(c)
  1999  #define SyisGraph(c)     isgraph(c)
  2000  #define SyDigToHex(c)    "0123456789ABCDEF"[c & 0x0F] 		
  2001  #define SyDigToInt(c)     ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 )
  2002  #define SyCharToUpper(c)  ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c)
  2003  #define SyCharToLower(c)  ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c)
  2004  /* Remove white space/NUL byte from a raw string */
  2005  #define SyStringLeftTrim(RAW)\
  2006  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
  2007  		(RAW)->nByte--;\
  2008  		(RAW)->zString++;\
  2009  	}
  2010  #define SyStringLeftTrimSafe(RAW)\
  2011  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
  2012  		(RAW)->nByte--;\
  2013  		(RAW)->zString++;\
  2014  	}
  2015  #define SyStringRightTrim(RAW)\
  2016  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
  2017  		(RAW)->nByte--;\
  2018  	}
  2019  #define SyStringRightTrimSafe(RAW)\
  2020  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && \
  2021  	(( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
  2022  		(RAW)->nByte--;\
  2023  	}
  2024  
  2025  #define SyStringFullTrim(RAW)\
  2026  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0  && SyisSpace((RAW)->zString[0])){\
  2027  		(RAW)->nByte--;\
  2028  		(RAW)->zString++;\
  2029  	}\
  2030  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
  2031  		(RAW)->nByte--;\
  2032  	}
  2033  #define SyStringFullTrimSafe(RAW)\
  2034  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0  && \
  2035            ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
  2036  		(RAW)->nByte--;\
  2037  		(RAW)->zString++;\
  2038  	}\
  2039  	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && \
  2040                     ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
  2041  		(RAW)->nByte--;\
  2042  	}
  2043  #ifndef JX9_DISABLE_BUILTIN_FUNC
  2044  /* 
  2045   * An XML raw text, CDATA, tag name and son is parsed out and stored
  2046   * in an instance of the following structure.
  2047   */
  2048  typedef struct SyXMLRawStr SyXMLRawStr;
  2049  struct SyXMLRawStr
  2050  {
  2051  	const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */
  2052  	sxu32 nByte; /* Text length */
  2053  	sxu32 nLine; /* Line number this text occurs */
  2054  };
  2055  /*
  2056   * Event callback signatures.
  2057   */
  2058  typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr *, SyXMLRawStr *, sxu32, SyXMLRawStr *, void *);
  2059  typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *, void *);
  2060  typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
  2061  typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
  2062  typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *, void *);
  2063  typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *, int, SyToken *, void *);
  2064  typedef sxi32 (*ProcXMLStartDocument)(void *);
  2065  typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *, SyXMLRawStr *, void *);
  2066  typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *, void *);
  2067  typedef sxi32 (*ProcXMLEndDocument)(void *);
  2068  /* XML processing control flags */
  2069  #define SXML_ENABLE_NAMESPACE	    0x01 /* Parse XML with namespace support enbaled */
  2070  #define SXML_ENABLE_QUERY		    0x02 /* Not used */	
  2071  #define SXML_OPTION_CASE_FOLDING    0x04 /* Controls whether case-folding is enabled for this XML parser */
  2072  #define SXML_OPTION_SKIP_TAGSTART   0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/
  2073  #define SXML_OPTION_SKIP_WHITE      0x10 /* Whether to skip values consisting of whitespace characters. */
  2074  #define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */
  2075  /* XML error codes */
  2076  enum xml_err_code{
  2077      SXML_ERROR_NONE = 1, 
  2078      SXML_ERROR_NO_MEMORY, 
  2079      SXML_ERROR_SYNTAX, 
  2080      SXML_ERROR_NO_ELEMENTS, 
  2081      SXML_ERROR_INVALID_TOKEN, 
  2082      SXML_ERROR_UNCLOSED_TOKEN, 
  2083      SXML_ERROR_PARTIAL_CHAR, 
  2084      SXML_ERROR_TAG_MISMATCH, 
  2085      SXML_ERROR_DUPLICATE_ATTRIBUTE, 
  2086      SXML_ERROR_JUNK_AFTER_DOC_ELEMENT, 
  2087      SXML_ERROR_PARAM_ENTITY_REF, 
  2088      SXML_ERROR_UNDEFINED_ENTITY, 
  2089      SXML_ERROR_RECURSIVE_ENTITY_REF, 
  2090      SXML_ERROR_ASYNC_ENTITY, 
  2091      SXML_ERROR_BAD_CHAR_REF, 
  2092      SXML_ERROR_BINARY_ENTITY_REF, 
  2093      SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, 
  2094      SXML_ERROR_MISPLACED_XML_PI, 
  2095      SXML_ERROR_UNKNOWN_ENCODING, 
  2096      SXML_ERROR_INCORRECT_ENCODING, 
  2097      SXML_ERROR_UNCLOSED_CDATA_SECTION, 
  2098      SXML_ERROR_EXTERNAL_ENTITY_HANDLING
  2099  };
  2100  /* Each active XML SAX parser is represented by an instance 
  2101   * of the following structure.
  2102   */
  2103  typedef struct SyXMLParser SyXMLParser;
  2104  struct SyXMLParser
  2105  {
  2106  	SyMemBackend *pAllocator; /* Memory backend */
  2107  	void *pUserData;          /* User private data forwarded varbatim by the XML parser
  2108  					           * as the last argument to the users callbacks.
  2109  						       */
  2110  	SyHash hns;               /* Namespace hashtable */
  2111  	SySet sToken;             /* XML tokens */
  2112  	SyLex sLex;               /* Lexical analyzer */
  2113  	sxi32 nFlags;             /* Control flags */
  2114  	/* User callbacks */
  2115  	ProcXMLStartTagHandler    xStartTag;     /* Start element handler */
  2116  	ProcXMLEndTagHandler      xEndTag;       /* End element handler */
  2117  	ProcXMLTextHandler        xRaw;          /* Raw text/CDATA handler   */
  2118  	ProcXMLDoctypeHandler     xDoctype;      /* DOCTYPE handler */
  2119  	ProcXMLPIHandler          xPi;           /* Processing instruction (PI) handler*/
  2120  	ProcXMLSyntaxErrorHandler xError;        /* Error handler */
  2121  	ProcXMLStartDocument      xStartDoc;     /* StartDoc handler */
  2122  	ProcXMLEndDocument        xEndDoc;       /* EndDoc handler */
  2123  	ProcXMLNameSpaceStart   xNameSpace;    /* Namespace declaration handler  */
  2124  	ProcXMLNameSpaceEnd       xNameSpaceEnd; /* End namespace declaration handler */
  2125  };
  2126  /*
  2127   * --------------
  2128   * Archive extractor:
  2129   * --------------
  2130   * Each open ZIP/TAR archive is identified by an instance of the following structure.
  2131   * That is, a process can open one or more archives and manipulates them in thread safe
  2132   * way by simply working with pointers to the following structure.
  2133   * Each entry in the archive is remembered in a hashtable.
  2134   * Lookup is very fast and entry with the same name are chained together.
  2135   */
  2136   typedef struct SyArchiveEntry SyArchiveEntry;
  2137   typedef struct SyArchive SyArchive;
  2138   struct SyArchive
  2139   {
  2140   	SyMemBackend	*pAllocator; /* Memory backend */
  2141  	SyArchiveEntry *pCursor;     /* Cursor for linear traversal of archive entries */
  2142  	SyArchiveEntry *pList;       /* Pointer to the List of the loaded archive */
  2143  	SyArchiveEntry **apHash;     /* Hashtable for archive entry */
  2144  	ProcRawStrCmp xCmp;          /* Hash comparison function */
  2145  	ProcHash xHash;              /* Hash Function */
  2146  	sxu32 nSize;        /* Hashtable size */
  2147   	sxu32 nEntry;       /* Total number of entries in the zip/tar archive */
  2148   	sxu32 nLoaded;      /* Total number of entries loaded in memory */
  2149   	sxu32 nCentralOfft;	/* Central directory offset(ZIP only. Otherwise Zero) */
  2150   	sxu32 nCentralSize;	/* Central directory size(ZIP only. Otherwise Zero) */
  2151  	void *pUserData;    /* Upper layer private data */
  2152  	sxu32 nMagic;       /* Sanity check */
  2153  	
  2154   };
  2155  #define SXARCH_MAGIC	0xDEAD635A
  2156  #define SXARCH_INVALID(ARCH)            (ARCH == 0  || ARCH->nMagic != SXARCH_MAGIC)
  2157  #define SXARCH_ENTRY_INVALID(ENTRY)	    (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC)
  2158  #define SyArchiveHashFunc(ARCH)	        (ARCH)->xHash
  2159  #define SyArchiveCmpFunc(ARCH)	        (ARCH)->xCmp
  2160  #define SyArchiveUserData(ARCH)         (ARCH)->pUserData
  2161  #define SyArchiveSetUserData(ARCH, DATA) (ARCH)->pUserData = DATA
  2162  /*
  2163   * Each loaded archive record is identified by an instance
  2164   * of the following structure.
  2165   */
  2166   struct SyArchiveEntry
  2167   { 	
  2168   	sxu32 nByte;         /* Contents size before compression */
  2169   	sxu32 nByteCompr;    /* Contents size after compression */
  2170  	sxu32 nReadCount;    /* Read counter */
  2171   	sxu32 nCrc;          /* Contents CRC32  */
  2172   	Sytm  sFmt;	         /* Last-modification time */
  2173   	sxu32 nOfft;         /* Data offset. */
  2174   	sxu16 nComprMeth;	 /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/
  2175   	sxu16 nExtra;        /* Extra size if any */
  2176   	SyString sFileName;  /* entry name & length */
  2177   	sxu32 nDup;	/* Total number of entries with the same name */
  2178  	SyArchiveEntry *pNextHash, *pPrevHash; /* Hash collision chains */
  2179   	SyArchiveEntry *pNextName;    /* Next entry with the same name */
  2180  	SyArchiveEntry *pNext, *pPrev; /* Next and previous entry in the list */
  2181  	sxu32 nHash;     /* Hash of the entry name */
  2182   	void *pUserData; /* User data */ 
  2183  	sxu32 nMagic;    /* Sanity check */
  2184   };
  2185   /*
  2186   * Extra flags for extending the file local header
  2187   */ 
  2188  #define SXZIP_EXTRA_TIMESTAMP	0x001	/* Extended UNIX timestamp */
  2189  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2190  #ifndef JX9_DISABLE_HASH_FUNC
  2191  /* MD5 context */
  2192  typedef struct MD5Context MD5Context;
  2193  struct MD5Context {
  2194   sxu32 buf[4];
  2195   sxu32 bits[2];
  2196   unsigned char in[64];
  2197  };
  2198  /* SHA1 context */
  2199  typedef struct SHA1Context SHA1Context;
  2200  struct SHA1Context {
  2201    unsigned int state[5];
  2202    unsigned int count[2];
  2203    unsigned char buffer[64];
  2204  };
  2205  #endif /* JX9_DISABLE_HASH_FUNC */
  2206  /* JX9 private declaration */
  2207  /*
  2208   * Memory Objects.
  2209   * Internally, the JX9 virtual machine manipulates nearly all JX9 values
  2210   * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures.
  2211   * Each jx9_values struct may cache multiple representations (string, integer etc.)
  2212   * of the same value.
  2213   */
  2214  struct jx9_value
  2215  {
  2216  	union{
  2217  		jx9_real rVal;  /* Real value */
  2218  		sxi64 iVal;     /* Integer value */
  2219  		void *pOther;   /* Other values (Object, Array, Resource, Namespace, etc.) */
  2220  	}x;
  2221  	sxi32 iFlags;       /* Control flags (see below) */
  2222  	jx9_vm *pVm;        /* VM this instance belong */
  2223  	SyBlob sBlob;       /* String values */
  2224  	sxu32 nIdx;         /* Object index in the global pool */
  2225  };
  2226  /* Allowed value types.
  2227   */
  2228  #define MEMOBJ_STRING    0x001  /* Memory value is a UTF-8 string */
  2229  #define MEMOBJ_INT       0x002  /* Memory value is an integer */
  2230  #define MEMOBJ_REAL      0x004  /* Memory value is a real number */
  2231  #define MEMOBJ_BOOL      0x008  /* Memory value is a boolean */
  2232  #define MEMOBJ_NULL      0x020  /* Memory value is NULL */
  2233  #define MEMOBJ_HASHMAP   0x040  /* Memory value is a hashmap (JSON representation of Array and Objects)  */
  2234  #define MEMOBJ_RES       0x100  /* Memory value is a resource [User private data] */
  2235  /* Mask of all known types */
  2236  #define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) 
  2237  /* Scalar variables
  2238   * According to the JX9 language reference manual
  2239   *  Scalar variables are those containing an integer, float, string or boolean.
  2240   *  Types array, object and resource are not scalar. 
  2241   */
  2242  #define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL)
  2243  /*
  2244   * The following macro clear the current jx9_value type and replace
  2245   * it with the given one.
  2246   */
  2247  #define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE)
  2248  /* jx9_value cast method signature */
  2249  typedef sxi32 (*ProcMemObjCast)(jx9_value *);
  2250  /* Forward reference */
  2251  typedef struct jx9_output_consumer jx9_output_consumer;
  2252  typedef struct jx9_user_func jx9_user_func;
  2253  typedef struct jx9_conf jx9_conf;
  2254  /*
  2255   * An instance of the following structure store the default VM output 
  2256   * consumer and it's private data.
  2257   * Client-programs can register their own output consumer callback
  2258   * via the [JX9_VM_CONFIG_OUTPUT] configuration directive.
  2259   * Please refer to the official documentation for more information
  2260   * on how to register an output consumer callback.
  2261   */
  2262  struct jx9_output_consumer
  2263  {
  2264  	ProcConsumer xConsumer; /* VM output consumer routine */
  2265  	void *pUserData;        /* Third argument to xConsumer() */
  2266  	ProcConsumer xDef;      /* Default output consumer routine */
  2267  	void *pDefData;         /* Third argument to xDef() */
  2268  };
  2269  /*
  2270   * JX9 engine [i.e: jx9 instance] configuration is stored in
  2271   * an instance of the following structure.
  2272   * Please refer to the official documentation for more information
  2273   * on how to configure your jx9 engine instance.
  2274   */
  2275  struct jx9_conf
  2276  {
  2277  	ProcConsumer xErr;   /* Compile-time error consumer callback */
  2278  	void *pErrData;      /* Third argument to xErr() */
  2279  	SyBlob sErrConsumer; /* Default error consumer */
  2280  };
  2281  /*
  2282   * Signature of the C function responsible of expanding constant values.
  2283   */
  2284  typedef void (*ProcConstant)(jx9_value *, void *);
  2285  /*
  2286   * Each registered constant [i.e: __TIME__, __DATE__, JX9_OS, INT_MAX, etc.] is stored
  2287   * in an instance of the following structure.
  2288   * Please refer to the official documentation for more information
  2289   * on how to create/install foreign constants.
  2290   */
  2291  typedef struct jx9_constant jx9_constant;
  2292  struct jx9_constant
  2293  {
  2294  	SyString sName;        /* Constant name */
  2295  	ProcConstant xExpand;  /* Function responsible of expanding constant value */
  2296  	void *pUserData;       /* Last argument to xExpand() */
  2297  };
  2298  typedef struct jx9_aux_data jx9_aux_data;
  2299  /*
  2300   * Auxiliary data associated with each foreign function is stored
  2301   * in a stack of the following structure.
  2302   * Note that automatic tracked chunks are also stored in an instance
  2303   * of this structure.
  2304   */
  2305  struct jx9_aux_data
  2306  {
  2307  	void *pAuxData; /* Aux data */
  2308  };
  2309  /* Foreign functions signature */
  2310  typedef int (*ProcHostFunction)(jx9_context *, int, jx9_value **);
  2311  /*
  2312   * Each installed foreign function is recored in an instance of the following
  2313   * structure.
  2314   * Please refer to the official documentation for more information on how 
  2315   * to create/install foreign functions.
  2316   */
  2317  struct jx9_user_func
  2318  {
  2319  	jx9_vm *pVm;              /* VM that own this instance */
  2320  	SyString sName;           /* Foreign function name */
  2321  	ProcHostFunction xFunc;  /* Implementation of the foreign function */
  2322  	void *pUserData;          /* User private data [Refer to the official documentation for more information]*/
  2323  	SySet aAux;               /* Stack of auxiliary data [Refer to the official documentation for more information]*/
  2324  };
  2325  /*
  2326   * The 'context' argument for an installable function. A pointer to an
  2327   * instance of this structure is the first argument to the routines used
  2328   * implement the foreign functions.
  2329   */
  2330  struct jx9_context
  2331  {
  2332  	jx9_user_func *pFunc;   /* Function information. */
  2333  	jx9_value *pRet;        /* Return value is stored here. */
  2334  	SySet sVar;             /* Container of dynamically allocated jx9_values
  2335  							 * [i.e: Garbage collection purposes.]
  2336  							 */
  2337  	SySet sChunk;           /* Track dynamically allocated chunks [jx9_aux_data instance]. 
  2338  							 * [i.e: Garbage collection purposes.]
  2339  							 */
  2340  	jx9_vm *pVm;            /* Virtual machine that own this context */
  2341  	sxi32 iFlags;           /* Call flags */
  2342  };
  2343  /* Hashmap control flags */
  2344  #define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/
  2345  /*
  2346   * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance
  2347   * of the following structure.
  2348   */
  2349  struct jx9_hashmap_node
  2350  {
  2351  	jx9_hashmap *pMap;     /* Hashmap that own this instance */
  2352  	sxi32 iType;           /* Node type */
  2353  	union{
  2354  		sxi64 iKey;        /* Int key */
  2355  		SyBlob sKey;       /* Blob key */
  2356  	}xKey;
  2357  	sxi32 iFlags;          /* Control flags */
  2358  	sxu32 nHash;           /* Key hash value */
  2359  	sxu32 nValIdx;         /* Value stored in this node */
  2360  	jx9_hashmap_node *pNext, *pPrev;               /* Link to other entries [i.e: linear traversal] */
  2361  	jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */
  2362  };
  2363  /* 
  2364   * Each active hashmap aka array in the JX9 jargon is represented
  2365   * by an instance of the following structure.
  2366   */
  2367  struct jx9_hashmap
  2368  {
  2369  	jx9_vm *pVm;                  /* VM that own this instance */
  2370  	jx9_hashmap_node **apBucket;  /* Hash bucket */
  2371  	jx9_hashmap_node *pFirst;     /* First inserted entry */
  2372  	jx9_hashmap_node *pLast;      /* Last inserted entry */
  2373  	jx9_hashmap_node *pCur;       /* Current entry */
  2374  	sxu32 nSize;                  /* Bucket size */
  2375  	sxu32 nEntry;                 /* Total number of inserted entries */
  2376  	sxu32 (*xIntHash)(sxi64);     /* Hash function for int_keys */
  2377  	sxu32 (*xBlobHash)(const void *, sxu32); /* Hash function for blob_keys */
  2378  	sxi32 iFlags;                 /* Hashmap control flags */
  2379  	sxi64 iNextIdx;               /* Next available automatically assigned index */
  2380  	sxi32 iRef;                   /* Reference count */
  2381  };
  2382  /* An instance of the following structure is the context
  2383   * for the FOREACH_STEP/FOREACH_INIT VM instructions.
  2384   * Those instructions are used to implement the 'foreach'
  2385   * statement.
  2386   * This structure is made available to these instructions
  2387   * as the P3 operand. 
  2388   */
  2389  struct jx9_foreach_info
  2390  {
  2391  	SyString sKey;      /* Key name. Empty otherwise*/
  2392  	SyString sValue;    /* Value name */
  2393  	sxi32 iFlags;       /* Control flags */
  2394  	SySet aStep;        /* Stack of steps [i.e: jx9_foreach_step instance] */
  2395  };
  2396  struct jx9_foreach_step
  2397  {
  2398  	sxi32 iFlags;                   /* Control flags (see below) */
  2399  	/* Iterate on this map*/
  2400  	jx9_hashmap *pMap;          /* Hashmap [i.e: array in the JX9 jargon] iteration
  2401  									 * Ex: foreach(array(1, 2, 3) as $key=>$value){} 
  2402  									 */
  2403  	
  2404  };
  2405  /* Foreach step control flags */
  2406  #define JX9_4EACH_STEP_KEY     0x001 /* Make Key available */
  2407  /*
  2408   * Each JX9 engine is identified by an instance of the following structure.
  2409   * Please refer to the official documentation for more information
  2410   * on how to configure your JX9 engine instance.
  2411   */
  2412  struct jx9
  2413  {
  2414  	SyMemBackend sAllocator;     /* Low level memory allocation subsystem */
  2415  	const jx9_vfs *pVfs;         /* Underlying Virtual File System */
  2416  	jx9_conf xConf;              /* Configuration */
  2417  #if defined(JX9_ENABLE_THREADS)
  2418  	SyMutex *pMutex;                 /* Per-engine mutex */
  2419  #endif
  2420  	jx9_vm *pVms;      /* List of active VM */
  2421  	sxi32 iVm;         /* Total number of active VM */
  2422  	jx9 *pNext, *pPrev; /* List of active engines */
  2423  	sxu32 nMagic;      /* Sanity check against misuse */
  2424  };
  2425  /* Code generation data structures */
  2426  typedef sxi32 (*ProcErrorGen)(void *, sxi32, sxu32, const char *, ...);
  2427  typedef struct jx9_expr_node   jx9_expr_node;
  2428  typedef struct jx9_expr_op     jx9_expr_op;
  2429  typedef struct jx9_gen_state   jx9_gen_state;
  2430  typedef struct GenBlock        GenBlock;
  2431  typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *);
  2432  typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32);
  2433  /*
  2434   * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented
  2435   * by an instance of the following structure.
  2436   * The JX9 parser does not use any external tools and is 100% handcoded.
  2437   * That is, the JX9 parser is thread-safe , full reentrant, produce consistant 
  2438   * compile-time errrors and at least 7 times faster than the standard JX9 parser.
  2439   */
  2440  struct jx9_expr_op
  2441  {
  2442  	SyString sOp;   /* String representation of the operator [i.e: "+", "*", "=="...] */
  2443  	sxi32 iOp;      /* Operator ID */
  2444  	sxi32 iPrec;    /* Operator precedence: 1 == Highest */ 
  2445  	sxi32 iAssoc;   /* Operator associativity (either left, right or non-associative) */ 
  2446  	sxi32 iVmOp;    /* VM OP code for this operator [i.e: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/
  2447  };
  2448  /*
  2449   * Each expression node is parsed out and recorded
  2450   * in an instance of the following structure.
  2451   */
  2452  struct jx9_expr_node
  2453  {
  2454  	const jx9_expr_op *pOp;  /* Operator ID or NULL if literal, constant, variable, function or object method call */
  2455  	jx9_expr_node *pLeft;    /* Left expression tree */
  2456  	jx9_expr_node *pRight;   /* Right expression tree */
  2457  	SyToken *pStart;         /* Stream of tokens that belong to this node */
  2458  	SyToken *pEnd;           /* End of token stream */
  2459  	sxi32 iFlags;            /* Node construct flags */
  2460  	ProcNodeConstruct xCode; /* C routine responsible of compiling this node */
  2461  	SySet aNodeArgs;         /* Node arguments. Only used by postfix operators [i.e: function call]*/
  2462  	jx9_expr_node *pCond;    /* Condition: Only used by the ternary operator '?:' */
  2463  };
  2464  /* Node Construct flags */
  2465  #define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i, --$j] node */
  2466  /*
  2467   * A block of instructions is recorded in an instance of the following structure.
  2468   * This structure is used only during compile-time and have no meaning
  2469   * during bytecode execution.
  2470   */
  2471  struct GenBlock
  2472  {
  2473  	jx9_gen_state *pGen;  /* State of the code generator */
  2474  	GenBlock *pParent;    /* Upper block or NULL if global */
  2475  	sxu32 nFirstInstr;    /* First instruction to execute  */
  2476  	sxi32 iFlags;         /* Block control flags (see below) */
  2477  	SySet aJumpFix;       /* Jump fixup (JumpFixup instance) */
  2478  	void *pUserData;      /* Upper layer private data */
  2479  	/* The following two fields are used only when compiling 
  2480  	 * the 'do..while()' language construct.
  2481  	 */
  2482  	sxu8 bPostContinue;    /* TRUE when compiling the do..while() statement */
  2483  	SySet aPostContFix;    /* Post-continue jump fix */
  2484  };
  2485  /*
  2486   * Code generator state is remembered in an instance of the following
  2487   * structure. We put the information in this structure and pass around
  2488   * a pointer to this structure, rather than pass around  all of the 
  2489   * information separately. This helps reduce the number of  arguments
  2490   * to generator functions.
  2491   * This structure is used only during compile-time and have no meaning
  2492   * during bytecode execution.
  2493   */
  2494  struct jx9_gen_state
  2495  {
  2496  	jx9_vm *pVm;         /* VM that own this instance */
  2497  	SyHash hLiteral;     /* Constant string Literals table */
  2498  	SyHash hNumLiteral;  /* Numeric literals table */
  2499  	SyHash hVar;         /* Collected variable hashtable */
  2500  	GenBlock *pCurrent;  /* Current processed block */
  2501  	GenBlock sGlobal;    /* Global block */
  2502  	ProcConsumer xErr;   /* Error consumer callback */
  2503  	void *pErrData;      /* Third argument to xErr() */
  2504  	SyToken *pIn;        /* Current processed token */
  2505  	SyToken *pEnd;       /* Last token in the stream */
  2506  	sxu32 nErr;          /* Total number of compilation error */
  2507  };
  2508  /* Forward references */
  2509  typedef struct jx9_vm_func_static_var  jx9_vm_func_static_var;
  2510  typedef struct jx9_vm_func_arg jx9_vm_func_arg;
  2511  typedef struct jx9_vm_func jx9_vm_func;
  2512  typedef struct VmFrame VmFrame;
  2513  /*
  2514   * Each collected function argument is recorded in an instance
  2515   * of the following structure.
  2516   * Note that as an extension, JX9 implements full type hinting
  2517   * which mean that any function can have it's own signature.
  2518   * Example:
  2519   *      function foo(int $a, string $b, float $c, ClassInstance $d){}
  2520   * This is how the powerful function overloading mechanism is
  2521   * implemented.
  2522   * Note that as an extension, JX9 allow function arguments to have
  2523   * any complex default value associated with them unlike the standard
  2524   * JX9 engine.
  2525   * Example:
  2526   *    function foo(int $a = rand() & 1023){}
  2527   *    now, when foo is called without arguments [i.e: foo()] the
  2528   *    $a variable (first parameter) will be set to a random number
  2529   *    between 0 and 1023 inclusive.
  2530   * Refer to the official documentation for more information on this
  2531   * mechanism and other extension introduced by the JX9 engine.
  2532   */
  2533  struct jx9_vm_func_arg
  2534  {
  2535  	SyString sName;      /* Argument name */
  2536  	SySet aByteCode;     /* Compiled default value associated with this argument */
  2537  	sxu32 nType;         /* Type of this argument [i.e: array, int, string, float, object, etc.] */
  2538  	sxi32 iFlags;        /* Configuration flags */
  2539  };
  2540  /*
  2541   * Each static variable is parsed out and remembered in an instance
  2542   * of the following structure.
  2543   * Note that as an extension, JX9 allow static variable have
  2544   * any complex default value associated with them unlike the standard
  2545   * JX9 engine.
  2546   * Example:
  2547   *   static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with 
  2548   *                                         // a random three characters(English alphabet)
  2549   *   dump($rand_str);
  2550   *   //You should see something like this
  2551   *   string(6 'JX9awt');   
  2552   */
  2553  struct jx9_vm_func_static_var
  2554  {
  2555  	SyString sName;   /* Static variable name */
  2556  	SySet aByteCode;  /* Compiled initialization expression  */
  2557  	sxu32 nIdx;       /* Object index in the global memory object container */
  2558  };
  2559  /* Function configuration flags */
  2560  #define VM_FUNC_ARG_HAS_DEF  0x001 /* Argument has default value associated with it */
  2561  #define VM_FUNC_ARG_IGNORE   0x002 /* Do not install argument in the current frame */
  2562  /*
  2563   * Each user defined function is parsed out and stored in an instance
  2564   * of the following structure.
  2565   * JX9 introduced some powerfull extensions to the JX9 5 programming
  2566   * language like function overloading, type hinting, complex default
  2567   * arguments values and many more.
  2568   * Please refer to the official documentation for more information.
  2569   */
  2570  struct jx9_vm_func
  2571  {
  2572  	SySet aArgs;         /* Expected arguments (jx9_vm_func_arg instance) */
  2573  	SySet aStatic;       /* Static variable (jx9_vm_func_static_var instance) */
  2574  	SyString sName;      /* Function name */
  2575  	SySet aByteCode;     /* Compiled function body */
  2576  	sxi32 iFlags;        /* VM function configuration */
  2577  	SyString sSignature; /* Function signature used to implement function overloading
  2578  						  * (Refer to the official docuemntation for more information
  2579  						  *  on this powerfull feature)
  2580  						  */
  2581  	void *pUserData;     /* Upper layer private data associated with this instance */
  2582  	jx9_vm_func *pNextName; /* Next VM function with the same name as this one */
  2583  };
  2584  /* Forward reference */
  2585  typedef struct jx9_builtin_constant jx9_builtin_constant;
  2586  typedef struct jx9_builtin_func jx9_builtin_func;
  2587  /*
  2588   * Each built-in foreign function (C function) is stored in an
  2589   * instance of the following structure.
  2590   * Please refer to the official documentation for more information
  2591   * on how to create/install foreign functions.
  2592   */
  2593  struct jx9_builtin_func
  2594  {
  2595  	const char *zName;        /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/
  2596  	ProcHostFunction xFunc;  /* C routine performing the computation */
  2597  };
  2598  /*
  2599   * Each built-in foreign constant is stored in an instance
  2600   * of the following structure.
  2601   * Please refer to the official documentation for more information
  2602   * on how to create/install foreign constants.
  2603   */
  2604  struct jx9_builtin_constant
  2605  {
  2606  	const char *zName;     /* Constant name */
  2607  	ProcConstant xExpand;  /* C routine responsible of expanding constant value*/
  2608  };
  2609  /*
  2610   * A single instruction of the virtual machine has an opcode
  2611   * and as many as three operands.
  2612   * Each VM instruction resulting from compiling a JX9 script
  2613   * is stored in an instance of the following structure.
  2614   */
  2615  typedef struct VmInstr VmInstr;
  2616  struct VmInstr
  2617  {
  2618  	sxu8  iOp; /* Operation to preform */
  2619  	sxi32 iP1; /* First operand */
  2620  	sxu32 iP2; /* Second operand (Often the jump destination) */
  2621  	void *p3;  /* Third operand (Often Upper layer private data) */
  2622  };
  2623  /* Forward reference */
  2624  typedef struct jx9_case_expr jx9_case_expr;
  2625  typedef struct jx9_switch jx9_switch;
  2626  /*
  2627   * Each compiled case block in a swicth statement is compiled
  2628   * and stored in an instance of the following structure.
  2629   */
  2630  struct jx9_case_expr
  2631  {
  2632  	SySet aByteCode;   /* Compiled body of the case block */
  2633  	sxu32 nStart;      /* First instruction to execute */
  2634  };
  2635  /*
  2636   * Each compiled switch statement is parsed out and stored
  2637   * in an instance of the following structure.
  2638   */
  2639  struct jx9_switch
  2640  {
  2641  	SySet aCaseExpr;  /* Compile case block */
  2642  	sxu32 nOut;       /* First instruction to execute after this statement */
  2643  	sxu32 nDefault;   /* First instruction to execute in the default block */
  2644  };
  2645  /* Assertion flags */
  2646  #define JX9_ASSERT_DISABLE    0x01  /* Disable assertion */
  2647  #define JX9_ASSERT_WARNING    0x02  /* Issue a warning for each failed assertion */
  2648  #define JX9_ASSERT_BAIL       0x04  /* Terminate execution on failed assertions */
  2649  #define JX9_ASSERT_QUIET_EVAL 0x08  /* Not used */
  2650  #define JX9_ASSERT_CALLBACK   0x10  /* Callback to call on failed assertions */
  2651  /* 
  2652   * An instance of the following structure hold the bytecode instructions
  2653   * resulting from compiling a JX9 script.
  2654   * This structure contains the complete state of the virtual machine.
  2655   */
  2656  struct jx9_vm
  2657  {
  2658  	SyMemBackend sAllocator;	/* Memory backend */
  2659  #if defined(JX9_ENABLE_THREADS)
  2660  	SyMutex *pMutex;           /* Recursive mutex associated with this VM. */
  2661  #endif
  2662  	jx9 *pEngine;               /* Interpreter that own this VM */
  2663  	SySet aByteCode;            /* Default bytecode container */
  2664  	SySet *pByteContainer;      /* Current bytecode container */
  2665  	VmFrame *pFrame;            /* Stack of active frames */
  2666  	SyPRNGCtx sPrng;            /* PRNG context */
  2667  	SySet aMemObj;              /* Object allocation table */
  2668  	SySet aLitObj;              /* Literals allocation table */
  2669  	jx9_value *aOps;            /* Operand stack */
  2670  	SySet aFreeObj;             /* Stack of free memory objects */
  2671  	SyHash hConstant;           /* Host-application and user defined constants container */
  2672  	SyHash hHostFunction;       /* Host-application installable functions */
  2673  	SyHash hFunction;           /* Compiled functions */
  2674  	SyHash hSuper;              /* Global variable */
  2675  	SyBlob sConsumer;           /* Default VM consumer [i.e Redirect all VM output to this blob] */
  2676  	SyBlob sWorker;             /* General purpose working buffer */
  2677  	SyBlob sArgv;               /* $argv[] collector [refer to the [getopt()] implementation for more information] */
  2678  	SySet aFiles;               /* Stack of processed files */
  2679  	SySet aPaths;               /* Set of import paths */
  2680  	SySet aIncluded;            /* Set of included files */
  2681  	SySet aIOstream;            /* Installed IO stream container */
  2682  	const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */
  2683  	jx9_value sExec;           /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/
  2684  	void *pStdin;              /* STDIN IO stream */
  2685  	void *pStdout;             /* STDOUT IO stream */
  2686  	void *pStderr;             /* STDERR IO stream */
  2687  	int bErrReport;            /* TRUE to report all runtime Error/Warning/Notice */
  2688  	int nRecursionDepth;       /* Current recursion depth */
  2689  	int nMaxDepth;             /* Maximum allowed recusion depth */
  2690  	sxu32 nOutputLen;          /* Total number of generated output */
  2691  	jx9_output_consumer sVmConsumer; /* Registered output consumer callback */
  2692  	int iAssertFlags;          /* Assertion flags */
  2693  	jx9_value sAssertCallback; /* Callback to call on failed assertions */
  2694  	sxi32 iExitStatus;         /* Script exit status */
  2695  	jx9_gen_state sCodeGen;    /* Code generator module */
  2696  	jx9_vm *pNext, *pPrev;      /* List of active VM's */
  2697  	sxu32 nMagic;              /* Sanity check against misuse */
  2698  };
  2699  /*
  2700   * Allowed value for jx9_vm.nMagic
  2701   */
  2702  #define JX9_VM_INIT   0xEA12CD72  /* VM correctly initialized */
  2703  #define JX9_VM_RUN    0xBA851227  /* VM ready to execute JX9 bytecode */
  2704  #define JX9_VM_EXEC   0xCDFE1DAD  /* VM executing JX9 bytecode */
  2705  #define JX9_VM_STALE  0xDEAD2BAD  /* Stale VM */
  2706  /*
  2707   * Error codes according to the JX9 language reference manual.
  2708   */
  2709  enum iErrCode
  2710  {
  2711  	E_ERROR             = 1,   /* Fatal run-time errors. These indicate errors that can not be recovered 
  2712  							    * from, such as a memory allocation problem. Execution of the script is
  2713  							    * halted.
  2714  								* The only fatal error under JX9 is an out-of-memory. All others erros
  2715  								* even a call to undefined function will not halt script execution.
  2716  							    */
  2717  	E_WARNING           ,   /* Run-time warnings (non-fatal errors). Execution of the script is not halted.  */
  2718  	E_PARSE             ,   /* Compile-time parse errors. Parse errors should only be generated by the parser.*/
  2719  	E_NOTICE            ,   /* Run-time notices. Indicate that the script encountered something that could 
  2720  							    * indicate an error, but could also happen in the normal course of running a script. 
  2721  							    */
  2722  };
  2723  /*
  2724   * Each VM instruction resulting from compiling a JX9 script is represented
  2725   * by one of the following OP codes.
  2726   * The program consists of a linear sequence of operations. Each operation
  2727   * has an opcode and 3 operands.Operands P1 is an integer.
  2728   * Operand P2 is an unsigned integer and operand P3 is a memory address.
  2729   * Few opcodes use all 3 operands.
  2730   */
  2731  enum jx9_vm_op {
  2732    JX9_OP_DONE =   1,   /* Done */
  2733    JX9_OP_HALT,         /* Halt */
  2734    JX9_OP_LOAD,         /* Load memory object */
  2735    JX9_OP_LOADC,        /* Load constant */
  2736    JX9_OP_LOAD_IDX,     /* Load array entry */   
  2737    JX9_OP_LOAD_MAP,     /* Load hashmap('array') */
  2738    JX9_OP_NOOP,         /* NOOP */
  2739    JX9_OP_JMP,          /* Unconditional jump */
  2740    JX9_OP_JZ,           /* Jump on zero (FALSE jump) */
  2741    JX9_OP_JNZ,          /* Jump on non-zero (TRUE jump) */
  2742    JX9_OP_POP,          /* Stack POP */ 
  2743    JX9_OP_CAT,          /* Concatenation */
  2744    JX9_OP_CVT_INT,      /* Integer cast */
  2745    JX9_OP_CVT_STR,      /* String cast */
  2746    JX9_OP_CVT_REAL,     /* Float cast */
  2747    JX9_OP_CALL,         /* Function call */
  2748    JX9_OP_UMINUS,       /* Unary minus '-'*/
  2749    JX9_OP_UPLUS,        /* Unary plus '+'*/
  2750    JX9_OP_BITNOT,       /* Bitwise not '~' */
  2751    JX9_OP_LNOT,         /* Logical not '!' */
  2752    JX9_OP_MUL,          /* Multiplication '*' */
  2753    JX9_OP_DIV,          /* Division '/' */
  2754    JX9_OP_MOD,          /* Modulus '%' */
  2755    JX9_OP_ADD,          /* Add '+' */
  2756    JX9_OP_SUB,          /* Sub '-' */
  2757    JX9_OP_SHL,          /* Left shift '<<' */
  2758    JX9_OP_SHR,          /* Right shift '>>' */
  2759    JX9_OP_LT,           /* Less than '<' */
  2760    JX9_OP_LE,           /* Less or equal '<=' */
  2761    JX9_OP_GT,           /* Greater than '>' */
  2762    JX9_OP_GE,           /* Greater or equal '>=' */
  2763    JX9_OP_EQ,           /* Equal '==' */
  2764    JX9_OP_NEQ,          /* Not equal '!=' */
  2765    JX9_OP_TEQ,          /* Type equal '===' */
  2766    JX9_OP_TNE,          /* Type not equal '!==' */
  2767    JX9_OP_BAND,         /* Bitwise and '&' */
  2768    JX9_OP_BXOR,         /* Bitwise xor '^' */
  2769    JX9_OP_BOR,          /* Bitwise or '|' */
  2770    JX9_OP_LAND,         /* Logical and '&&','and' */
  2771    JX9_OP_LOR,          /* Logical or  '||','or' */
  2772    JX9_OP_LXOR,         /* Logical xor 'xor' */
  2773    JX9_OP_STORE,        /* Store Object */
  2774    JX9_OP_STORE_IDX,    /* Store indexed object */
  2775    JX9_OP_PULL,         /* Stack pull */
  2776    JX9_OP_SWAP,         /* Stack swap */
  2777    JX9_OP_YIELD,        /* Stack yield */
  2778    JX9_OP_CVT_BOOL,     /* Boolean cast */
  2779    JX9_OP_CVT_NUMC,     /* Numeric (integer, real or both) type cast */
  2780    JX9_OP_INCR,         /* Increment ++ */
  2781    JX9_OP_DECR,         /* Decrement -- */
  2782    JX9_OP_ADD_STORE,    /* Add and store '+=' */
  2783    JX9_OP_SUB_STORE,    /* Sub and store '-=' */
  2784    JX9_OP_MUL_STORE,    /* Mul and store '*=' */
  2785    JX9_OP_DIV_STORE,    /* Div and store '/=' */
  2786    JX9_OP_MOD_STORE,    /* Mod and store '%=' */
  2787    JX9_OP_CAT_STORE,    /* Cat and store '.=' */
  2788    JX9_OP_SHL_STORE,    /* Shift left and store '>>=' */
  2789    JX9_OP_SHR_STORE,    /* Shift right and store '<<=' */
  2790    JX9_OP_BAND_STORE,   /* Bitand and store '&=' */
  2791    JX9_OP_BOR_STORE,    /* Bitor and store '|=' */
  2792    JX9_OP_BXOR_STORE,   /* Bitxor and store '^=' */
  2793    JX9_OP_CONSUME,      /* Consume VM output */
  2794    JX9_OP_MEMBER,       /* Object member run-time access */
  2795    JX9_OP_UPLINK,       /* Run-Time frame link */
  2796    JX9_OP_CVT_NULL,     /* NULL cast */
  2797    JX9_OP_CVT_ARRAY,    /* Array cast */
  2798    JX9_OP_FOREACH_INIT, /* For each init */
  2799    JX9_OP_FOREACH_STEP, /* For each step */
  2800    JX9_OP_SWITCH        /* Switch operation */
  2801  };
  2802  /* -- END-OF INSTRUCTIONS -- */
  2803  /*
  2804   * Expression Operators ID.
  2805   */
  2806  enum jx9_expr_id {
  2807  	EXPR_OP_DOT,      /* Member access */
  2808  	EXPR_OP_DC,        /* :: */
  2809  	EXPR_OP_SUBSCRIPT, /* []: Subscripting */
  2810  	EXPR_OP_FUNC_CALL, /* func_call() */
  2811  	EXPR_OP_INCR,      /* ++ */
  2812  	EXPR_OP_DECR,      /* -- */ 
  2813  	EXPR_OP_BITNOT,    /* ~ */
  2814  	EXPR_OP_UMINUS,    /* Unary minus  */
  2815  	EXPR_OP_UPLUS,     /* Unary plus */
  2816  	EXPR_OP_TYPECAST,  /* Type cast [i.e: (int), (float), (string)...] */
  2817  	EXPR_OP_ALT,       /* @ */
  2818  	EXPR_OP_INSTOF,    /* instanceof */
  2819  	EXPR_OP_LOGNOT,    /* logical not ! */
  2820  	EXPR_OP_MUL,       /* Multiplication */
  2821  	EXPR_OP_DIV,       /* division */
  2822  	EXPR_OP_MOD,       /* Modulus */
  2823  	EXPR_OP_ADD,       /* Addition */
  2824  	EXPR_OP_SUB,       /* Substraction */
  2825  	EXPR_OP_DDOT,      /* Concatenation */
  2826  	EXPR_OP_SHL,       /* Left shift */
  2827  	EXPR_OP_SHR,       /* Right shift */
  2828  	EXPR_OP_LT,        /* Less than */
  2829  	EXPR_OP_LE,        /* Less equal */
  2830  	EXPR_OP_GT,        /* Greater than */
  2831  	EXPR_OP_GE,        /* Greater equal */
  2832  	EXPR_OP_EQ,        /* Equal == */
  2833  	EXPR_OP_NE,        /* Not equal != <> */
  2834  	EXPR_OP_TEQ,       /* Type equal === */
  2835  	EXPR_OP_TNE,       /* Type not equal !== */
  2836  	EXPR_OP_SEQ,       /* String equal 'eq' */
  2837  	EXPR_OP_SNE,       /* String not equal 'ne' */
  2838  	EXPR_OP_BAND,      /* Biwise and '&' */
  2839  	EXPR_OP_REF,       /* Reference operator '&' */
  2840  	EXPR_OP_XOR,       /* bitwise xor '^' */
  2841  	EXPR_OP_BOR,       /* bitwise or '|' */
  2842  	EXPR_OP_LAND,      /* Logical and '&&','and' */
  2843  	EXPR_OP_LOR,       /* Logical or  '||','or'*/
  2844  	EXPR_OP_LXOR,      /* Logical xor 'xor' */
  2845  	EXPR_OP_QUESTY,    /* Ternary operator '?' */
  2846  	EXPR_OP_ASSIGN,    /* Assignment '=' */
  2847  	EXPR_OP_ADD_ASSIGN, /* Combined operator: += */
  2848  	EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */
  2849  	EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */
  2850  	EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */
  2851  	EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */
  2852  	EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */
  2853  	EXPR_OP_AND_ASSIGN, /* Combined operator: &= */
  2854  	EXPR_OP_OR_ASSIGN,  /* Combined operator: |= */
  2855  	EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */
  2856  	EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */
  2857  	EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */
  2858  	EXPR_OP_COMMA       /* Comma expression */
  2859  };
  2860  /*
  2861   * Lexer token codes
  2862   * The following set of constants are the tokens recognized
  2863   * by the lexer when processing JX9 input.
  2864   * Important: Token values MUST BE A POWER OF TWO.
  2865   */
  2866  #define JX9_TK_INTEGER   0x0000001  /* Integer */
  2867  #define JX9_TK_REAL      0x0000002  /* Real number */
  2868  #define JX9_TK_NUM       (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */
  2869  #define JX9_TK_KEYWORD   0x0000004 /* Keyword [i.e: while, for, if, foreach...] */
  2870  #define JX9_TK_ID        0x0000008 /* Alphanumeric or UTF-8 stream */
  2871  #define JX9_TK_DOLLAR    0x0000010 /* '$' Dollar sign */
  2872  #define JX9_TK_OP        0x0000020 /* Operator [i.e: +, *, /...] */
  2873  #define JX9_TK_OCB       0x0000040 /* Open curly brace'{' */
  2874  #define JX9_TK_CCB       0x0000080 /* Closing curly brace'}' */
  2875  #define JX9_TK_DOT       0x0000100 /* Dot . */
  2876  #define JX9_TK_LPAREN    0x0000200 /* Left parenthesis '(' */
  2877  #define JX9_TK_RPAREN    0x0000400 /* Right parenthesis ')' */
  2878  #define JX9_TK_OSB       0x0000800 /* Open square bracket '[' */
  2879  #define JX9_TK_CSB       0x0001000 /* Closing square bracket ']' */
  2880  #define JX9_TK_DSTR      0x0002000 /* Double quoted string "$str" */
  2881  #define JX9_TK_SSTR      0x0004000 /* Single quoted string 'str' */
  2882  #define JX9_TK_NOWDOC    0x0010000 /* Nowdoc <<< */
  2883  #define JX9_TK_COMMA     0x0020000 /* Comma ',' */
  2884  #define JX9_TK_SEMI      0x0040000 /* Semi-colon ";" */
  2885  #define JX9_TK_BSTR      0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */
  2886  #define JX9_TK_COLON     0x0100000 /* single Colon ':' */
  2887  #define JX9_TK_AMPER     0x0200000 /* Ampersand '&' */
  2888  #define JX9_TK_EQUAL     0x0400000 /* Equal '=' */
  2889  #define JX9_TK_OTHER     0x1000000 /* Other symbols */
  2890  /*
  2891   * JX9 keyword.
  2892   * These words have special meaning in JX9. Some of them represent things which look like
  2893   * functions, some look like constants, and so on, but they're not, really: they are language constructs.
  2894   * You cannot use any of the following words as constants, object names, function or method names.
  2895   * Using them as variable names is generally OK, but could lead to confusion. 
  2896   */
  2897  #define JX9_TKWRD_SWITCH       1 /* switch */
  2898  #define JX9_TKWRD_PRINT        2 /* print */
  2899  #define JX9_TKWRD_ELIF         0x4000000 /* elseif: MUST BE A POWER OF TWO */
  2900  #define JX9_TKWRD_ELSE         0x8000000 /* else:  MUST BE A POWER OF TWO */
  2901  #define JX9_TKWRD_IF           3 /* if */
  2902  #define JX9_TKWRD_STATIC       4 /* static */
  2903  #define JX9_TKWRD_CASE         5 /* case */
  2904  #define JX9_TKWRD_FUNCTION     6 /* function */
  2905  #define JX9_TKWRD_CONST        7 /* const */
  2906  /* The number '8' is reserved for JX9_TK_ID */
  2907  #define JX9_TKWRD_WHILE        9 /* while */
  2908  #define JX9_TKWRD_DEFAULT      10 /* default */
  2909  #define JX9_TKWRD_AS           11 /* as */
  2910  #define JX9_TKWRD_CONTINUE     12 /* continue */
  2911  #define JX9_TKWRD_EXIT         13 /* exit */
  2912  #define JX9_TKWRD_DIE          14 /* die */
  2913  #define JX9_TKWRD_IMPORT       15 /* import */
  2914  #define JX9_TKWRD_INCLUDE      16 /* include */
  2915  #define JX9_TKWRD_FOR          17 /* for */
  2916  #define JX9_TKWRD_FOREACH      18 /* foreach */
  2917  #define JX9_TKWRD_RETURN       19 /* return */
  2918  #define JX9_TKWRD_BREAK        20 /* break */
  2919  #define JX9_TKWRD_UPLINK       21 /* uplink */
  2920  #define JX9_TKWRD_BOOL         0x8000   /* bool:  MUST BE A POWER OF TWO */
  2921  #define JX9_TKWRD_INT          0x10000  /* int:   MUST BE A POWER OF TWO */
  2922  #define JX9_TKWRD_FLOAT        0x20000  /* float:  MUST BE A POWER OF TWO */
  2923  #define JX9_TKWRD_STRING       0x40000  /* string: MUST BE A POWER OF TWO */
  2924  
  2925  /* api.c */
  2926  JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap);
  2927  JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName);
  2928  JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName);
  2929  /* json.c function prototypes */
  2930  JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut);
  2931  JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte);
  2932  /* memobj.c function prototypes */
  2933  JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj);
  2934  JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal);
  2935  JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore);
  2936  JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest);
  2937  JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal);
  2938  JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray);
  2939  #if 0
  2940  /* Not used in the current release of the JX9 engine */
  2941  JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal);
  2942  #endif
  2943  JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal);
  2944  JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal);
  2945  JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj);
  2946  JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen);
  2947  #if 0
  2948  /* Not used in the current release of the JX9 engine */
  2949  JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap);
  2950  #endif
  2951  JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest);
  2952  JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest);
  2953  JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj);
  2954  JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj);
  2955  JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj);
  2956  JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags);
  2957  JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj);
  2958  JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj);
  2959  JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj);
  2960  JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj);
  2961  JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj);
  2962  JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj);
  2963  JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj);
  2964  JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj);
  2965  JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData);
  2966  /* lex.c function prototypes */
  2967  JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut);
  2968  /* vm.c function prototypes */
  2969  JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue);
  2970  JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte, 
  2971  	sxi32 iFlags, void *pUserData);
  2972  JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName);
  2973  JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData);
  2974  JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData);
  2975  JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);
  2976  JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex);
  2977  JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex);
  2978  JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString);
  2979  JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap);
  2980  JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap);
  2981  JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage);
  2982  JX9_PRIVATE void  jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData);
  2983  JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData);
  2984  JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine);
  2985  JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap);
  2986  JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm);
  2987  JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar);
  2988  JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm);
  2989  JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm);
  2990  JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm);
  2991  JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm);
  2992  JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm);
  2993  JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm);
  2994  JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex);
  2995  JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm);
  2996  JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer);
  2997  JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex);
  2998  JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm);
  2999  JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult);
  3000  JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...);
  3001  JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx);
  3002  JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen);
  3003  JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue);
  3004  JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew);
  3005  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3006  JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte);
  3007  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3008  JX9_PRIVATE int jx9Utf8Read(
  3009    const unsigned char *z,         /* First byte of UTF-8 character */
  3010    const unsigned char *zTerm,     /* Pretend this byte is 0x00 */
  3011    const unsigned char **pzNext    /* Write first byte past UTF-8 char here */
  3012  );
  3013  /* parse.c function prototypes */
  3014  JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID);
  3015  JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot);
  3016  JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext);
  3017  JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd);
  3018  JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast);
  3019  JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet);
  3020  /* compile.c function prototypes */
  3021  JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType);
  3022  JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3023  JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3024  JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3025  JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3026  JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3027  JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3028  JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3029  JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag);
  3030  JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
  3031  JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
  3032  JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...);
  3033  JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags);
  3034  /* constant.c function prototypes */
  3035  JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm);
  3036  /* builtin.c function prototypes */
  3037  JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm);
  3038  /* hashmap.c function prototypes */
  3039  JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32));
  3040  JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm);
  3041  JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS);
  3042  JX9_PRIVATE void  jx9HashmapUnref(jx9_hashmap *pMap);
  3043  JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode);
  3044  JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal);
  3045  JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight);
  3046  JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest);
  3047  JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict);
  3048  JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap);
  3049  JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap);
  3050  JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode);
  3051  JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore);
  3052  JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey);
  3053  JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm);
  3054  JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
  3055  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3056  JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut);
  3057  /* builtin.c function prototypes */ 
  3058  JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *), 
  3059  	jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf);
  3060  JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl, 
  3061  	int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData);
  3062  JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData);
  3063  JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen);
  3064  JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection);
  3065  #endif
  3066  /* vfs.c */
  3067  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3068  JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile, 
  3069  	int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew);
  3070  JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut);
  3071  JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle);
  3072  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3073  JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen);
  3074  JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm);
  3075  JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void);
  3076  JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm);
  3077  JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm);
  3078  JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm);
  3079  /* lib.c function prototypes */
  3080  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3081  JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp);
  3082  JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch);
  3083  JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch);
  3084  JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry);
  3085  JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen);
  3086  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3087  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3088  JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData);
  3089  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3090  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3091  #ifndef JX9_DISABLE_HASH_FUNC
  3092  JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen);
  3093  JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len);
  3094  JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx);
  3095  JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx);
  3096  JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]);
  3097  JX9_PRIVATE void SHA1Init(SHA1Context *context);
  3098  JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len);
  3099  JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]);
  3100  JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]);
  3101  #endif
  3102  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3103  JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen);
  3104  JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData);
  3105  JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...);
  3106  JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap);
  3107  JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...);
  3108  JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...);
  3109  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3110  JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth);
  3111  JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay);
  3112  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3113  JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8);
  3114  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3115  JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  3116  #endif
  3117  JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex);
  3118  JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp);
  3119  JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData);
  3120  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3121  JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  3122  JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  3123  #endif /* JX9_DISABLE_BUILTIN_FUNC */
  3124  JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen);
  3125  JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3126  JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3127  JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3128  JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3129  JX9_PRIVATE sxi32 SyHexToint(sxi32 c);
  3130  JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3131  JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  3132  JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail);
  3133  JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData);
  3134  JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData);
  3135  JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData);
  3136  JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen);
  3137  JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash);
  3138  JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp);
  3139  JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx);
  3140  JX9_PRIVATE void *SySetPop(SySet *pSet);
  3141  JX9_PRIVATE void *SySetPeek(SySet *pSet);
  3142  JX9_PRIVATE sxi32 SySetRelease(SySet *pSet);
  3143  JX9_PRIVATE sxi32 SySetReset(SySet *pSet);
  3144  JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet);
  3145  JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry);
  3146  JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem);
  3147  JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem);
  3148  JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize);
  3149  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3150  JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft);
  3151  #endif
  3152  JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob);
  3153  JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob);
  3154  JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen);
  3155  JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest);
  3156  JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob);
  3157  JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize);
  3158  JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte);
  3159  JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator);
  3160  JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize);
  3161  JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize);
  3162  JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize);
  3163  JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend);
  3164  JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData);
  3165  JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData);
  3166  JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent);
  3167  #if 0
  3168  /* Not used in the current release of the JX9 engine */
  3169  JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
  3170  #endif
  3171  JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk);
  3172  JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte);
  3173  JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
  3174  JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
  3175  JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte);
  3176  JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
  3177  JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
  3178  JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
  3179  JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen);
  3180  JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen);
  3181  #if !defined(JX9_DISABLE_BUILTIN_FUNC) || defined(__APPLE__)
  3182  JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen);
  3183  #endif
  3184  JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos);
  3185  #ifndef JX9_DISABLE_BUILTIN_FUNC
  3186  JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
  3187  #endif
  3188  JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
  3189  JX9_PRIVATE sxu32 SyStrlen(const char *zSrc);
  3190  #if defined(JX9_ENABLE_THREADS)
  3191  JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
  3192  JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
  3193  JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
  3194  #endif
  3195  JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb);
  3196  JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB);
  3197  JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb);
  3198  JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB);
  3199  JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64);
  3200  JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64);
  3201  JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64);
  3202  JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32);
  3203  JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16);
  3204  JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut);
  3205  JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut);
  3206  #endif /* __JX9INT_H__ */
  3207  
  3208  /*
  3209   * ----------------------------------------------------------
  3210   * File: unqliteInt.h
  3211   * MD5: 325816ce05f6adbaab2c39a41875dedd
  3212   * ----------------------------------------------------------
  3213   */
  3214  /*
  3215   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
  3216   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
  3217   * Version 1.1.6
  3218   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  3219   * please contact Symisc Systems via:
  3220   *       legal@symisc.net
  3221   *       licensing@symisc.net
  3222   *       contact@symisc.net
  3223   * or visit:
  3224   *      http://unqlite.org/licensing.html
  3225   */
  3226   /* $SymiscID: unqliteInt.h v1.7 FreeBSD 2012-11-02 11:25 devel <chm@symisc.net> $ */
  3227  #ifndef __UNQLITEINT_H__
  3228  #define __UNQLITEINT_H__
  3229  /* Internal interface definitions for UnQLite. */
  3230  #ifdef UNQLITE_AMALGAMATION
  3231  /* Marker for routines not intended for external use */
  3232  #define UNQLITE_PRIVATE static
  3233  #define JX9_AMALGAMATION
  3234  #else
  3235  #define UNQLITE_PRIVATE
  3236  #include "unqlite.h"
  3237  #include "jx9Int.h"
  3238  #endif 
  3239  /* forward declaration */
  3240  typedef struct unqlite_db unqlite_db;
  3241  /*
  3242  ** The following values may be passed as the second argument to
  3243  ** UnqliteOsLock(). The various locks exhibit the following semantics:
  3244  **
  3245  ** SHARED:    Any number of processes may hold a SHARED lock simultaneously.
  3246  ** RESERVED:  A single process may hold a RESERVED lock on a file at
  3247  **            any time. Other processes may hold and obtain new SHARED locks.
  3248  ** PENDING:   A single process may hold a PENDING lock on a file at
  3249  **            any one time. Existing SHARED locks may persist, but no new
  3250  **            SHARED locks may be obtained by other processes.
  3251  ** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
  3252  **
  3253  ** PENDING_LOCK may not be passed directly to UnqliteOsLock(). Instead, a
  3254  ** process that requests an EXCLUSIVE lock may actually obtain a PENDING
  3255  ** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
  3256  ** UnqliteOsLock().
  3257  */
  3258  #define NO_LOCK         0
  3259  #define SHARED_LOCK     1
  3260  #define RESERVED_LOCK   2
  3261  #define PENDING_LOCK    3
  3262  #define EXCLUSIVE_LOCK  4
  3263  /*
  3264   * UnQLite Locking Strategy (Same as SQLite3)
  3265   *
  3266   * The following #defines specify the range of bytes used for locking.
  3267   * SHARED_SIZE is the number of bytes available in the pool from which
  3268   * a random byte is selected for a shared lock.  The pool of bytes for
  3269   * shared locks begins at SHARED_FIRST. 
  3270   *
  3271   * The same locking strategy and byte ranges are used for Unix and Windows.
  3272   * This leaves open the possiblity of having clients on winNT, and
  3273   * unix all talking to the same shared file and all locking correctly.
  3274   * To do so would require that samba (or whatever
  3275   * tool is being used for file sharing) implements locks correctly between
  3276   * windows and unix.  I'm guessing that isn't likely to happen, but by
  3277   * using the same locking range we are at least open to the possibility.
  3278   *
  3279   * Locking in windows is mandatory.  For this reason, we cannot store
  3280   * actual data in the bytes used for locking.  The pager never allocates
  3281   * the pages involved in locking therefore.  SHARED_SIZE is selected so
  3282   * that all locks will fit on a single page even at the minimum page size.
  3283   * PENDING_BYTE defines the beginning of the locks.  By default PENDING_BYTE
  3284   * is set high so that we don't have to allocate an unused page except
  3285   * for very large databases.  But one should test the page skipping logic 
  3286   * by setting PENDING_BYTE low and running the entire regression suite.
  3287   *
  3288   * Changing the value of PENDING_BYTE results in a subtly incompatible
  3289   * file format.  Depending on how it is changed, you might not notice
  3290   * the incompatibility right away, even running a full regression test.
  3291   * The default location of PENDING_BYTE is the first byte past the
  3292   * 1GB boundary.
  3293   */
  3294  #define PENDING_BYTE     (0x40000000)
  3295  #define RESERVED_BYTE    (PENDING_BYTE+1)
  3296  #define SHARED_FIRST     (PENDING_BYTE+2)
  3297  #define SHARED_SIZE      510
  3298  /*
  3299   * The default size of a disk sector in bytes.
  3300   */
  3301  #ifndef UNQLITE_DEFAULT_SECTOR_SIZE
  3302  #define UNQLITE_DEFAULT_SECTOR_SIZE 512
  3303  #endif
  3304  /*
  3305   * Each open database file is managed by a separate instance
  3306   * of the "Pager" structure.
  3307   */
  3308  typedef struct Pager Pager;
  3309  /*
  3310   * Each database file to be accessed by the system is an instance
  3311   * of the following structure.
  3312   */
  3313  struct unqlite_db
  3314  {
  3315  	Pager *pPager;              /* Pager and Transaction manager */
  3316  	jx9 *pJx9;                  /* Jx9 Engine handle */
  3317  	unqlite_kv_cursor *pCursor; /* Database cursor for common usage */
  3318  };
  3319  /*
  3320   * Each database connection is an instance of the following structure.
  3321   */
  3322  struct unqlite
  3323  {
  3324  	SyMemBackend sMem;              /* Memory allocator subsystem */
  3325  	SyBlob sErr;                    /* Error log */
  3326  	unqlite_db sDB;                 /* Storage backend */
  3327  #if defined(UNQLITE_ENABLE_THREADS)
  3328  	const SyMutexMethods *pMethods;  /* Mutex methods */
  3329  	SyMutex *pMutex;                 /* Per-handle mutex */
  3330  #endif
  3331  	unqlite_vm *pVms;                /* List of active VM */
  3332  	sxi32 iVm;                       /* Total number of active VM */
  3333  	sxi32 iFlags;                    /* Control flags (See below)  */
  3334  	unqlite *pNext,*pPrev;           /* List of active DB handles */
  3335  	sxu32 nMagic;                    /* Sanity check against misuse */
  3336  };
  3337  #define UNQLITE_FL_DISABLE_AUTO_COMMIT   0x001 /* Disable auto-commit on close */
  3338  /*
  3339   * VM control flags (Mostly related to collection handling).
  3340   */
  3341  #define UNQLITE_VM_COLLECTION_CREATE     0x001 /* Create a new collection */
  3342  #define UNQLITE_VM_COLLECTION_OVERWRITE  0x002 /* Overwrite old collection */
  3343  #define UNQLITE_VM_AUTO_LOAD             0x004 /* Auto load a collection from the vfs */
  3344  /* Forward declaration */
  3345  typedef struct unqlite_col_record unqlite_col_record;
  3346  typedef struct unqlite_col unqlite_col;
  3347  /*
  3348   * Each an in-memory collection record is stored in an instance
  3349   * of the following structure.
  3350   */
  3351  struct unqlite_col_record
  3352  {
  3353  	unqlite_col *pCol;                      /* Collecion this record belong */
  3354  	jx9_int64 nId;                          /* Unique record ID */
  3355  	jx9_value sValue;                       /* In-memory value of the record */
  3356  	unqlite_col_record *pNextCol,*pPrevCol; /* Collision chain */
  3357  	unqlite_col_record *pNext,*pPrev;       /* Linked list of records */
  3358  };
  3359  /* 
  3360   * Magic number to identify a valid collection on disk.
  3361   */
  3362  #define UNQLITE_COLLECTION_MAGIC 0x611E /* sizeof(unsigned short) 2 bytes */
  3363  /*
  3364   * A loaded collection is identified by an instance of the following structure.
  3365   */
  3366  struct unqlite_col
  3367  {
  3368  	unqlite_vm *pVm;   /* VM that own this instance */
  3369  	SyString sName;    /* ID of the collection */
  3370  	sxu32 nHash;       /* sName hash */
  3371  	jx9_value sSchema; /* Collection schema */
  3372  	sxu32 nSchemaOfft; /* Shema offset in sHeader */
  3373  	SyBlob sWorker;    /* General purpose working buffer */
  3374  	SyBlob sHeader;    /* Collection binary header */
  3375  	jx9_int64 nLastid; /* Last collection record ID */
  3376  	jx9_int64 nCurid;  /* Current record ID */
  3377  	jx9_int64 nTotRec; /* Total number of records in the collection */
  3378  	int iFlags;        /* Control flags (see below) */
  3379  	unqlite_col_record **apRecord; /* Hashtable of loaded records */
  3380  	unqlite_col_record *pList;     /* Linked list of records */
  3381  	sxu32 nRec;        /* Total number of records in apRecord[] */     
  3382  	sxu32 nRecSize;    /* apRecord[] size */
  3383  	Sytm sCreation;    /* Colleation creation time */
  3384  	unqlite_kv_cursor *pCursor; /* Cursor pointing to the raw binary data */
  3385  	unqlite_col *pNext,*pPrev;  /* Next and previous collection in the chain */
  3386  	unqlite_col *pNextCol,*pPrevCol; /* Collision chain */
  3387  };
  3388  /*
  3389   * Each unQLite Virtual Machine resulting from successful compilation of
  3390   * a Jx9 script is represented by an instance of the following structure.
  3391   */
  3392  struct unqlite_vm
  3393  {
  3394  	unqlite *pDb;              /* Database handle that own this instance */
  3395  	SyMemBackend sAlloc;       /* Private memory allocator */
  3396  #if defined(UNQLITE_ENABLE_THREADS)
  3397  	SyMutex *pMutex;           /* Recursive mutex associated with this VM. */
  3398  #endif
  3399  	unqlite_col **apCol;       /* Table of loaded collections */
  3400  	unqlite_col *pCol;         /* List of loaded collections */
  3401  	sxu32 iCol;                /* Total number of loaded collections */
  3402  	sxu32 iColSize;            /* apCol[] size  */
  3403  	jx9_vm *pJx9Vm;            /* Compiled Jx9 script*/
  3404  	unqlite_vm *pNext,*pPrev;  /* Linked list of active unQLite VM */
  3405  	sxu32 nMagic;              /* Magic number to avoid misuse */
  3406  };
  3407  /* 
  3408   * Database signature to identify a valid database image.
  3409   */
  3410  #define UNQLITE_DB_SIG "unqlite"
  3411  /*
  3412   * Database magic number (4 bytes).
  3413   */
  3414  #define UNQLITE_DB_MAGIC   0xDB7C2712
  3415  /*
  3416   * Maximum page size in bytes.
  3417   */
  3418  #ifdef UNQLITE_MAX_PAGE_SIZE
  3419  # undef UNQLITE_MAX_PAGE_SIZE
  3420  #endif
  3421  #define UNQLITE_MAX_PAGE_SIZE 65536 /* 65K */
  3422  /*
  3423   * Minimum page size in bytes.
  3424   */
  3425  #ifdef UNQLITE_MIN_PAGE_SIZE
  3426  # undef UNQLITE_MIN_PAGE_SIZE
  3427  #endif
  3428  #define UNQLITE_MIN_PAGE_SIZE 512
  3429  /*
  3430   * The default size of a database page.
  3431   */
  3432  #ifndef UNQLITE_DEFAULT_PAGE_SIZE
  3433  # undef UNQLITE_DEFAULT_PAGE_SIZE
  3434  #endif
  3435  # define UNQLITE_DEFAULT_PAGE_SIZE 4096 /* 4K */
  3436  /* Forward declaration */
  3437  typedef struct Bitvec Bitvec;
  3438  /* Private library functions */
  3439  /* api.c */
  3440  UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void);
  3441  UNQLITE_PRIVATE int unqliteDataConsumer(
  3442  	const void *pOut,   /* Data to consume */
  3443  	unsigned int nLen,  /* Data length */
  3444  	void *pUserData     /* User private data */
  3445  	);
  3446  UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
  3447  	const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
  3448  	sxu32 nByte        /* zName length */
  3449  	);
  3450  UNQLITE_PRIVATE int unqliteGetPageSize(void);
  3451  UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr);
  3452  UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...);
  3453  UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb);
  3454  /* unql_vm.c */
  3455  UNQLITE_PRIVATE int unqliteCreateCollection(unqlite_vm *pVm,SyString *pName);
  3456  UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol);
  3457  UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol);
  3458  UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(unqlite_col *pCol,jx9_int64 nId);
  3459  UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol);
  3460  UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol);
  3461  UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue);
  3462  UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(unqlite_col *pCol,jx9_int64 nId,jx9_value *pValue);
  3463  UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(unqlite_vm *pVm,SyString *pCol,int iFlag);
  3464  UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue);
  3465  UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag);
  3466  UNQLITE_PRIVATE int unqliteCollectionDropRecord(unqlite_col *pCol,jx9_int64 nId,int wr_header,int log_err);
  3467  UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol);
  3468  /* unql_jx9.c */
  3469  UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm);
  3470  /* fastjson.c */
  3471  UNQLITE_PRIVATE sxi32 FastJsonEncode(
  3472  	jx9_value *pValue, /* Value to encode */
  3473  	SyBlob *pOut,      /* Store encoded value here */
  3474  	int iNest          /* Nesting limit */ 
  3475  	);
  3476  UNQLITE_PRIVATE sxi32 FastJsonDecode(
  3477  	const void *pIn, /* Binary JSON  */
  3478  	sxu32 nByte,     /* Chunk delimiter */
  3479  	jx9_value *pOut, /* Decoded value */
  3480  	const unsigned char **pzPtr,
  3481  	int iNest /* Nesting limit */
  3482  	);
  3483  /* vfs.c [io_win.c, io_unix.c ] */
  3484  UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void);
  3485  /* mem_kv.c */
  3486  UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void);
  3487  /* lhash_kv.c */
  3488  UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void);
  3489  /* os.c */
  3490  UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
  3491  UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
  3492  UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size);
  3493  UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags);
  3494  UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize);
  3495  UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType);
  3496  UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType);
  3497  UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut);
  3498  UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id);
  3499  UNQLITE_PRIVATE int unqliteOsOpen(
  3500    unqlite_vfs *pVfs,
  3501    SyMemBackend *pAlloc,
  3502    const char *zPath, 
  3503    unqlite_file **ppOut, 
  3504    unsigned int flags
  3505  );
  3506  UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId);
  3507  UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync);
  3508  UNQLITE_PRIVATE int unqliteOsAccess(unqlite_vfs *pVfs,const char *zPath,int flags,int *pResOut);
  3509  /* bitmap.c */
  3510  UNQLITE_PRIVATE Bitvec *unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize);
  3511  UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i);
  3512  UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i);
  3513  UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p);
  3514  /* pager.c */
  3515  UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut);
  3516  UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur);
  3517  UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage);
  3518  UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager);
  3519  UNQLITE_PRIVATE int unqlitePagerOpen(
  3520    unqlite_vfs *pVfs,       /* The virtual file system to use */
  3521    unqlite *pDb,            /* Database handle */
  3522    const char *zFilename,   /* Name of the database file to open */
  3523    unsigned int iFlags      /* flags controlling this file */
  3524    );
  3525  UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods);
  3526  UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb);
  3527  UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager);
  3528  UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager);
  3529  UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine);
  3530  UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen);
  3531  UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager);
  3532  #endif /* __UNQLITEINT_H__ */
  3533  /*
  3534   * ----------------------------------------------------------
  3535   * File: api.c
  3536   * MD5: d79e8404e50dacd0ea75635c1ebe553a
  3537   * ----------------------------------------------------------
  3538   */
  3539  /*
  3540   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
  3541   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
  3542   * Version 1.1.6
  3543   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  3544   * please contact Symisc Systems via:
  3545   *       legal@symisc.net
  3546   *       licensing@symisc.net
  3547   *       contact@symisc.net
  3548   * or visit:
  3549   *      http://unqlite.org/licensing.html
  3550   */
  3551   /* $SymiscID: api.c v2.0 FreeBSD 2012-11-08 23:07 stable <chm@symisc.net> $ */
  3552  #ifndef UNQLITE_AMALGAMATION
  3553  #include "unqliteInt.h"
  3554  #endif
  3555  /* This file implement the public interfaces presented to host-applications.
  3556   * Routines in other files are for internal use by UnQLite and should not be
  3557   * accessed by users of the library.
  3558   */
  3559  #define UNQLITE_DB_MISUSE(DB) (DB == 0 || DB->nMagic != UNQLITE_DB_MAGIC)
  3560  #define UNQLITE_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
  3561  /* If another thread have released a working instance, the following macros
  3562   * evaluates to true. These macros are only used when the library
  3563   * is built with threading support enabled.
  3564   */
  3565  #define UNQLITE_THRD_DB_RELEASE(DB) (DB->nMagic != UNQLITE_DB_MAGIC)
  3566  #define UNQLITE_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
  3567  /* IMPLEMENTATION: unqlite@embedded@symisc 118-09-4785 */
  3568  /*
  3569   * All global variables are collected in the structure named "sUnqlMPGlobal".
  3570   * That way it is clear in the code when we are using static variable because
  3571   * its name start with sUnqlMPGlobal.
  3572   */
  3573  static struct unqlGlobal_Data
  3574  {
  3575  	SyMemBackend sAllocator;                /* Global low level memory allocator */
  3576  #if defined(UNQLITE_ENABLE_THREADS)
  3577  	const SyMutexMethods *pMutexMethods;   /* Mutex methods */
  3578  	SyMutex *pMutex;                       /* Global mutex */
  3579  	sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded 
  3580  										    * The threading level can be set using the [unqlite_lib_config()]
  3581  											* interface with a configuration verb set to
  3582  											* UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or 
  3583  											* UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
  3584  											*/
  3585  #endif
  3586  	SySet kv_storage;                      /* Installed KV storage engines */
  3587  	int iPageSize;                         /* Default Page size */
  3588  	unqlite_vfs *pVfs;                     /* Underlying virtual file system (Vfs) */
  3589  	sxi32 nDB;                             /* Total number of active DB handles */
  3590  	unqlite *pDB;                          /* List of active DB handles */
  3591  	sxu32 nMagic;                          /* Sanity check against library misuse */
  3592  }sUnqlMPGlobal = {
  3593  	{0, 0, 0, 0, 0, 0, 0, 0, {0}}, 
  3594  #if defined(UNQLITE_ENABLE_THREADS)
  3595  	0, 
  3596  	0, 
  3597  	0, 
  3598  #endif
  3599  	{0, 0, 0, 0, 0, 0, 0 },
  3600  	UNQLITE_DEFAULT_PAGE_SIZE,
  3601  	0, 
  3602  	0, 
  3603  	0, 
  3604  	0
  3605  };
  3606  #define UNQLITE_LIB_MAGIC  0xEA1495BA
  3607  #define UNQLITE_LIB_MISUSE (sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC)
  3608  /*
  3609   * Supported threading level.
  3610   * These options have meaning only when the library is compiled with multi-threading
  3611   * support. That is, the UNQLITE_ENABLE_THREADS compile time directive must be defined
  3612   * when UnQLite is built.
  3613   * UNQLITE_THREAD_LEVEL_SINGLE:
  3614   *  In this mode, mutexing is disabled and the library can only be used by a single thread.
  3615   * UNQLITE_THREAD_LEVEL_MULTI
  3616   *  In this mode, all mutexes including the recursive mutexes on [unqlite] objects
  3617   *  are enabled so that the application is free to share the same database handle
  3618   *  between different threads at the same time.
  3619   */
  3620  #define UNQLITE_THREAD_LEVEL_SINGLE 1 
  3621  #define UNQLITE_THREAD_LEVEL_MULTI  2
  3622  /*
  3623   * Find a Key Value storage engine from the set of installed engines.
  3624   * Return a pointer to the storage engine methods on success. NULL on failure.
  3625   */
  3626  UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
  3627  	const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
  3628  	sxu32 nByte /* zName length */
  3629  	)
  3630  {
  3631  	unqlite_kv_methods **apStore,*pEntry;
  3632  	sxu32 n,nMax;
  3633  	/* Point to the set of installed engines */
  3634  	apStore = (unqlite_kv_methods **)SySetBasePtr(&sUnqlMPGlobal.kv_storage);
  3635  	nMax = SySetUsed(&sUnqlMPGlobal.kv_storage);
  3636  	for( n = 0 ; n < nMax; ++n ){
  3637  		pEntry = apStore[n];
  3638  		if( nByte == SyStrlen(pEntry->zName) && SyStrnicmp(pEntry->zName,zName,nByte) == 0 ){
  3639  			/* Storage engine found */
  3640  			return pEntry;
  3641  		}
  3642  	}
  3643  	/* No such entry, return NULL */
  3644  	return 0;
  3645  }
  3646  /*
  3647   * Configure the UnQLite library.
  3648   * Return UNQLITE_OK on success. Any other return value indicates failure.
  3649   * Refer to [unqlite_lib_config()].
  3650   */
  3651  static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap)
  3652  {
  3653  	int rc = UNQLITE_OK;
  3654  	switch(nOp){
  3655  	    case UNQLITE_LIB_CONFIG_PAGE_SIZE: {
  3656  			/* Default page size: Must be a power of two */
  3657  			int iPage = va_arg(ap,int);
  3658  			if( iPage >= UNQLITE_MIN_PAGE_SIZE && iPage <= UNQLITE_MAX_PAGE_SIZE ){
  3659  				if( !(iPage & (iPage - 1)) ){
  3660  					sUnqlMPGlobal.iPageSize = iPage;
  3661  				}else{
  3662  					/* Invalid page size */
  3663  					rc = UNQLITE_INVALID;
  3664  				}
  3665  			}else{
  3666  				/* Invalid page size */
  3667  				rc = UNQLITE_INVALID;
  3668  			}
  3669  			break;
  3670  										   }
  3671  	    case UNQLITE_LIB_CONFIG_STORAGE_ENGINE: {
  3672  			/* Install a key value storage engine */
  3673  			unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *);
  3674  			/* Make sure we are delaing with a valid methods */
  3675  			if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0
  3676  				|| pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0 
  3677  				|| pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){
  3678  					rc = UNQLITE_INVALID;
  3679  					break;
  3680  			}
  3681  			/* Install it */
  3682  			rc = SySetPut(&sUnqlMPGlobal.kv_storage,(const void *)&pMethods);
  3683  			break;
  3684  												}
  3685  	    case UNQLITE_LIB_CONFIG_VFS:{
  3686  			/* Install a virtual file system */
  3687  			unqlite_vfs *pVfs = va_arg(ap,unqlite_vfs *);
  3688  			if( pVfs ){
  3689  			 sUnqlMPGlobal.pVfs = pVfs;
  3690  			}
  3691  			break;
  3692  								}
  3693  		case UNQLITE_LIB_CONFIG_USER_MALLOC: {
  3694  			/* Use an alternative low-level memory allocation routines */
  3695  			const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
  3696  			/* Save the memory failure callback (if available) */
  3697  			ProcMemError xMemErr = sUnqlMPGlobal.sAllocator.xMemError;
  3698  			void *pMemErr = sUnqlMPGlobal.sAllocator.pUserData;
  3699  			if( pMethods == 0 ){
  3700  				/* Use the built-in memory allocation subsystem */
  3701  				rc = SyMemBackendInit(&sUnqlMPGlobal.sAllocator, xMemErr, pMemErr);
  3702  			}else{
  3703  				rc = SyMemBackendInitFromOthers(&sUnqlMPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
  3704  			}
  3705  			break;
  3706  										  }
  3707  		case UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK: {
  3708  			/* Memory failure callback */
  3709  			ProcMemError xMemErr = va_arg(ap, ProcMemError);
  3710  			void *pUserData = va_arg(ap, void *);
  3711  			sUnqlMPGlobal.sAllocator.xMemError = xMemErr;
  3712  			sUnqlMPGlobal.sAllocator.pUserData = pUserData;
  3713  			break;
  3714  												 }	  
  3715  		case UNQLITE_LIB_CONFIG_USER_MUTEX: {
  3716  #if defined(UNQLITE_ENABLE_THREADS)
  3717  			/* Use an alternative low-level mutex subsystem */
  3718  			const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
  3719  #if defined (UNTRUST)
  3720  			if( pMethods == 0 ){
  3721  				rc = UNQLITE_CORRUPT;
  3722  			}
  3723  #endif
  3724  			/* Sanity check */
  3725  			if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
  3726  				/* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
  3727  				rc = UNQLITE_CORRUPT;
  3728  				break;
  3729  			}
  3730  			if( sUnqlMPGlobal.pMutexMethods ){
  3731  				/* Overwrite the previous mutex subsystem */
  3732  				SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
  3733  				if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
  3734  					sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
  3735  				}
  3736  				sUnqlMPGlobal.pMutex = 0;
  3737  			}
  3738  			/* Initialize and install the new mutex subsystem */
  3739  			if( pMethods->xGlobalInit ){
  3740  				rc = pMethods->xGlobalInit();
  3741  				if ( rc != UNQLITE_OK ){
  3742  					break;
  3743  				}
  3744  			}
  3745  			/* Create the global mutex */
  3746  			sUnqlMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
  3747  			if( sUnqlMPGlobal.pMutex == 0 ){
  3748  				/*
  3749  				 * If the supplied mutex subsystem is so sick that we are unable to
  3750  				 * create a single mutex, there is no much we can do here.
  3751  				 */
  3752  				if( pMethods->xGlobalRelease ){
  3753  					pMethods->xGlobalRelease();
  3754  				}
  3755  				rc = UNQLITE_CORRUPT;
  3756  				break;
  3757  			}
  3758  			sUnqlMPGlobal.pMutexMethods = pMethods;			
  3759  			if( sUnqlMPGlobal.nThreadingLevel == 0 ){
  3760  				/* Set a default threading level */
  3761  				sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; 
  3762  			}
  3763  #endif
  3764  			break;
  3765  										   }
  3766  		case UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE:
  3767  #if defined(UNQLITE_ENABLE_THREADS)
  3768  			/* Single thread mode (Only one thread is allowed to play with the library) */
  3769  			sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_SINGLE;
  3770  			jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE);
  3771  #endif
  3772  			break;
  3773  		case UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI:
  3774  #if defined(UNQLITE_ENABLE_THREADS)
  3775  			/* Multi-threading mode (library is thread safe and database handles and virtual machines
  3776  			 * may be shared between multiple threads).
  3777  			 */
  3778  			sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
  3779  			jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_MULTI);
  3780  #endif
  3781  			break;
  3782  		default:
  3783  			/* Unknown configuration option */
  3784  			rc = UNQLITE_CORRUPT;
  3785  			break;
  3786  	}
  3787  	return rc;
  3788  }
  3789  /*
  3790   * [CAPIREF: unqlite_lib_config()]
  3791   * Please refer to the official documentation for function purpose and expected parameters.
  3792   */
  3793  int unqlite_lib_config(int nConfigOp,...)
  3794  {
  3795  	va_list ap;
  3796  	int rc;
  3797  	if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
  3798  		/* Library is already initialized, this operation is forbidden */
  3799  		return UNQLITE_LOCKED;
  3800  	}
  3801  	va_start(ap,nConfigOp);
  3802  	rc = unqliteCoreConfigure(nConfigOp,ap);
  3803  	va_end(ap);
  3804  	return rc;
  3805  }
  3806  /*
  3807   * Global library initialization
  3808   * Refer to [unqlite_lib_init()]
  3809   * This routine must be called to initialize the memory allocation subsystem, the mutex 
  3810   * subsystem prior to doing any serious work with the library. The first thread to call
  3811   * this routine does the initialization process and set the magic number so no body later
  3812   * can re-initialize the library. If subsequent threads call this  routine before the first
  3813   * thread have finished the initialization process, then the subsequent threads must block 
  3814   * until the initialization process is done.
  3815   */
  3816  static sxi32 unqliteCoreInitialize(void)
  3817  {
  3818  	const unqlite_kv_methods *pMethods;
  3819  	const unqlite_vfs *pVfs; /* Built-in vfs */
  3820  #if defined(UNQLITE_ENABLE_THREADS)
  3821  	const SyMutexMethods *pMutexMethods = 0;
  3822  	SyMutex *pMaster = 0;
  3823  #endif
  3824  	int rc;
  3825  	/*
  3826  	 * If the library is already initialized, then a call to this routine
  3827  	 * is a no-op.
  3828  	 */
  3829  	if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
  3830  		return UNQLITE_OK; /* Already initialized */
  3831  	}
  3832  	/* Point to the built-in vfs */
  3833  	pVfs = unqliteExportBuiltinVfs();
  3834  	/* Install it */
  3835  	unqlite_lib_config(UNQLITE_LIB_CONFIG_VFS, pVfs);
  3836  #if defined(UNQLITE_ENABLE_THREADS)
  3837  	if( sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_SINGLE ){
  3838  		pMutexMethods = sUnqlMPGlobal.pMutexMethods;
  3839  		if( pMutexMethods == 0 ){
  3840  			/* Use the built-in mutex subsystem */
  3841  			pMutexMethods = SyMutexExportMethods();
  3842  			if( pMutexMethods == 0 ){
  3843  				return UNQLITE_CORRUPT; /* Can't happen */
  3844  			}
  3845  			/* Install the mutex subsystem */
  3846  			rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MUTEX, pMutexMethods);
  3847  			if( rc != UNQLITE_OK ){
  3848  				return rc;
  3849  			}
  3850  		}
  3851  		/* Obtain a static mutex so we can initialize the library without calling malloc() */
  3852  		pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
  3853  		if( pMaster == 0 ){
  3854  			return UNQLITE_CORRUPT; /* Can't happen */
  3855  		}
  3856  	}
  3857  	/* Lock the master mutex */
  3858  	rc = UNQLITE_OK;
  3859  	SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  3860  	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
  3861  #endif
  3862  		if( sUnqlMPGlobal.sAllocator.pMethods == 0 ){
  3863  			/* Install a memory subsystem */
  3864  			rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
  3865  			if( rc != UNQLITE_OK ){
  3866  				/* If we are unable to initialize the memory backend, there is no much we can do here.*/
  3867  				goto End;
  3868  			}
  3869  		}
  3870  #if defined(UNQLITE_ENABLE_THREADS)
  3871  		if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
  3872  			/* Protect the memory allocation subsystem */
  3873  			rc = SyMemBackendMakeThreadSafe(&sUnqlMPGlobal.sAllocator, sUnqlMPGlobal.pMutexMethods);
  3874  			if( rc != UNQLITE_OK ){
  3875  				goto End;
  3876  			}
  3877  		}
  3878  #endif
  3879  		SySetInit(&sUnqlMPGlobal.kv_storage,&sUnqlMPGlobal.sAllocator,sizeof(unqlite_kv_methods *));
  3880  		/* Install the built-in Key Value storage engines */
  3881  		pMethods = unqliteExportMemKvStorage(); /* In-memory storage */
  3882  		unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
  3883  		/* Default disk key/value storage engine */
  3884  		pMethods = unqliteExportDiskKvStorage(); /* Disk storage */
  3885  		unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
  3886  		/* Default page size */
  3887  		if( sUnqlMPGlobal.iPageSize < UNQLITE_MIN_PAGE_SIZE ){
  3888  			unqlite_lib_config(UNQLITE_LIB_CONFIG_PAGE_SIZE,UNQLITE_DEFAULT_PAGE_SIZE);
  3889  		}
  3890  		/* Our library is initialized, set the magic number */
  3891  		sUnqlMPGlobal.nMagic = UNQLITE_LIB_MAGIC;
  3892  		rc = UNQLITE_OK;
  3893  #if defined(UNQLITE_ENABLE_THREADS)
  3894  	} /* sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC */
  3895  #endif
  3896  End:
  3897  #if defined(UNQLITE_ENABLE_THREADS)
  3898  	/* Unlock the master mutex */
  3899  	SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  3900  #endif
  3901  	return rc;
  3902  }
  3903  /* Forward declaration */
  3904  static int unqliteVmRelease(unqlite_vm *pVm);
  3905  /*
  3906   * Release a single instance of an unqlite database handle.
  3907   */
  3908  static int unqliteDbRelease(unqlite *pDb)
  3909  {
  3910  	unqlite_db *pStore = &pDb->sDB;
  3911  	unqlite_vm *pVm,*pNext;
  3912  	int rc = UNQLITE_OK;
  3913  	if( (pDb->iFlags & UNQLITE_FL_DISABLE_AUTO_COMMIT) == 0 ){
  3914  		/* Commit any outstanding transaction */
  3915  		rc = unqlitePagerCommit(pStore->pPager);
  3916  		if( rc != UNQLITE_OK ){
  3917  			/* Rollback the transaction */
  3918  			rc = unqlitePagerRollback(pStore->pPager,FALSE);
  3919  		}
  3920  	}else{
  3921  		/* Rollback any outstanding transaction */
  3922  		rc = unqlitePagerRollback(pStore->pPager,FALSE);
  3923  	}
  3924  	/* Close the pager */
  3925  	unqlitePagerClose(pStore->pPager);
  3926  	/* Release any active VM's */
  3927  	pVm = pDb->pVms;
  3928  	for(;;){
  3929  		if( pDb->iVm < 1 ){
  3930  			break;
  3931  		}
  3932  		/* Point to the next entry */
  3933  		pNext = pVm->pNext;
  3934  		unqliteVmRelease(pVm);
  3935  		pVm = pNext;
  3936  		pDb->iVm--;
  3937  	}
  3938  	/* Release the Jx9 handle */
  3939  	jx9_release(pStore->pJx9);
  3940  	/* Set a dummy magic number */
  3941  	pDb->nMagic = 0x7250;
  3942  	/* Release the whole memory subsystem */
  3943  	SyMemBackendRelease(&pDb->sMem);
  3944  	/* Commit or rollback result */
  3945  	return rc;
  3946  }
  3947  /*
  3948   * Release all resources consumed by the library.
  3949   * Note: This call is not thread safe. Refer to [unqlite_lib_shutdown()].
  3950   */
  3951  static void unqliteCoreShutdown(void)
  3952  {
  3953  	unqlite *pDb, *pNext;
  3954  	/* Release all active databases handles */
  3955  	pDb = sUnqlMPGlobal.pDB;
  3956  	for(;;){
  3957  		if( sUnqlMPGlobal.nDB < 1 ){
  3958  			break;
  3959  		}
  3960  		pNext = pDb->pNext;
  3961  		unqliteDbRelease(pDb); 
  3962  		pDb = pNext;
  3963  		sUnqlMPGlobal.nDB--;
  3964  	}
  3965  	/* Release the storage methods container */
  3966  	SySetRelease(&sUnqlMPGlobal.kv_storage);
  3967  #if defined(UNQLITE_ENABLE_THREADS)
  3968  	/* Release the mutex subsystem */
  3969  	if( sUnqlMPGlobal.pMutexMethods ){
  3970  		if( sUnqlMPGlobal.pMutex ){
  3971  			SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
  3972  			sUnqlMPGlobal.pMutex = 0;
  3973  		}
  3974  		if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
  3975  			sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
  3976  		}
  3977  		sUnqlMPGlobal.pMutexMethods = 0;
  3978  	}
  3979  	sUnqlMPGlobal.nThreadingLevel = 0;
  3980  #endif
  3981  	if( sUnqlMPGlobal.sAllocator.pMethods ){
  3982  		/* Release the memory backend */
  3983  		SyMemBackendRelease(&sUnqlMPGlobal.sAllocator);
  3984  	}
  3985  	sUnqlMPGlobal.nMagic = 0x1764;
  3986  	/* Finally, shutdown the Jx9 library */
  3987  	jx9_lib_shutdown();
  3988  }
  3989  /*
  3990   * [CAPIREF: unqlite_lib_init()]
  3991   * Please refer to the official documentation for function purpose and expected parameters.
  3992   */
  3993  int unqlite_lib_init(void)
  3994  {
  3995  	int rc;
  3996  	rc = unqliteCoreInitialize();
  3997  	return rc;
  3998  }
  3999  /*
  4000   * [CAPIREF: unqlite_lib_shutdown()]
  4001   * Please refer to the official documentation for function purpose and expected parameters.
  4002   */
  4003  int unqlite_lib_shutdown(void)
  4004  {
  4005  	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
  4006  		/* Already shut */
  4007  		return UNQLITE_OK;
  4008  	}
  4009  	unqliteCoreShutdown();
  4010  	return UNQLITE_OK;
  4011  }
  4012  /*
  4013   * [CAPIREF: unqlite_lib_is_threadsafe()]
  4014   * Please refer to the official documentation for function purpose and expected parameters.
  4015   */
  4016  int unqlite_lib_is_threadsafe(void)
  4017  {
  4018  	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
  4019  		return 0;
  4020  	}
  4021  #if defined(UNQLITE_ENABLE_THREADS)
  4022  		if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
  4023  			/* Muli-threading support is enabled */
  4024  			return 1;
  4025  		}else{
  4026  			/* Single-threading */
  4027  			return 0;
  4028  		}
  4029  #else
  4030  	return 0;
  4031  #endif
  4032  }
  4033  /*
  4034   *
  4035   * [CAPIREF: unqlite_lib_version()]
  4036   * Please refer to the official documentation for function purpose and expected parameters.
  4037   */
  4038  const char * unqlite_lib_version(void)
  4039  {
  4040  	return UNQLITE_VERSION;
  4041  }
  4042  /*
  4043   *
  4044   * [CAPIREF: unqlite_lib_signature()]
  4045   * Please refer to the official documentation for function purpose and expected parameters.
  4046   */
  4047  const char * unqlite_lib_signature(void)
  4048  {
  4049  	return UNQLITE_SIG;
  4050  }
  4051  /*
  4052   *
  4053   * [CAPIREF: unqlite_lib_ident()]
  4054   * Please refer to the official documentation for function purpose and expected parameters.
  4055   */
  4056  const char * unqlite_lib_ident(void)
  4057  {
  4058  	return UNQLITE_IDENT;
  4059  }
  4060  /*
  4061   *
  4062   * [CAPIREF: unqlite_lib_copyright()]
  4063   * Please refer to the official documentation for function purpose and expected parameters.
  4064   */
  4065  const char * unqlite_lib_copyright(void)
  4066  {
  4067  	return UNQLITE_COPYRIGHT;
  4068  }
  4069  /*
  4070   * Remove harmfull and/or stale flags passed to the [unqlite_open()] interface.
  4071   */
  4072  static unsigned int unqliteSanityzeFlag(unsigned int iFlags)
  4073  {
  4074  	iFlags &= ~UNQLITE_OPEN_EXCLUSIVE; /* Reserved flag */
  4075  	if( iFlags & UNQLITE_OPEN_TEMP_DB ){
  4076  		/* Omit journaling for temporary database */
  4077  		iFlags |= UNQLITE_OPEN_OMIT_JOURNALING|UNQLITE_OPEN_CREATE;
  4078  	}
  4079  	if( (iFlags & (UNQLITE_OPEN_READONLY|UNQLITE_OPEN_READWRITE)) == 0 ){
  4080  		/* Auto-append the R+W flag */
  4081  		iFlags |= UNQLITE_OPEN_READWRITE;
  4082  	}
  4083  	if( iFlags & UNQLITE_OPEN_CREATE ){
  4084  		iFlags &= ~(UNQLITE_OPEN_MMAP|UNQLITE_OPEN_READONLY);
  4085  		/* Auto-append the R+W flag */
  4086  		iFlags |= UNQLITE_OPEN_READWRITE;
  4087  	}else{
  4088  		if( iFlags & UNQLITE_OPEN_READONLY ){
  4089  			iFlags &= ~UNQLITE_OPEN_READWRITE;
  4090  		}else if( iFlags & UNQLITE_OPEN_READWRITE ){
  4091  			iFlags &= ~UNQLITE_OPEN_MMAP;
  4092  		}
  4093  	}
  4094  	return iFlags;
  4095  }
  4096  /*
  4097   * This routine does the work of initializing a database handle on behalf
  4098   * of [unqlite_open()].
  4099   */
  4100  static int unqliteInitDatabase(
  4101  	unqlite *pDB,            /* Database handle */
  4102  	SyMemBackend *pParent,   /* Master memory backend */
  4103  	const char *zFilename,   /* Target database */
  4104  	unsigned int iFlags      /* Open flags */
  4105  	)
  4106  {
  4107  	unqlite_db *pStorage = &pDB->sDB;
  4108  	int rc;
  4109  	/* Initialiaze the memory subsystem */
  4110  	SyMemBackendInitFromParent(&pDB->sMem,pParent);
  4111  #if defined(UNQLITE_ENABLE_THREADS)
  4112  	/* No need for internal mutexes */
  4113  	SyMemBackendDisbaleMutexing(&pDB->sMem);
  4114  #endif
  4115  	SyBlobInit(&pDB->sErr,&pDB->sMem);	
  4116  	/* Sanityze flags */
  4117  	iFlags = unqliteSanityzeFlag(iFlags);
  4118  	/* Init the pager and the transaction manager */
  4119  	rc = unqlitePagerOpen(sUnqlMPGlobal.pVfs,pDB,zFilename,iFlags);
  4120  	if( rc != UNQLITE_OK ){
  4121  		return rc;
  4122  	}
  4123  	/* Allocate a new Jx9 engine handle */
  4124  	rc = jx9_init(&pStorage->pJx9);
  4125  	if( rc != JX9_OK ){
  4126  		return rc;
  4127  	}
  4128  	return UNQLITE_OK;
  4129  }
  4130  /*
  4131   * Allocate and initialize a new UnQLite Virtual Mahcine and attach it
  4132   * to the compiled Jx9 script.
  4133   */
  4134  static int unqliteInitVm(unqlite *pDb,jx9_vm *pJx9Vm,unqlite_vm **ppOut)
  4135  {
  4136  	unqlite_vm *pVm;
  4137  
  4138  	*ppOut = 0;
  4139  	/* Allocate a new VM instance */
  4140  	pVm = (unqlite_vm *)SyMemBackendPoolAlloc(&pDb->sMem,sizeof(unqlite_vm));
  4141  	if( pVm == 0 ){
  4142  		return UNQLITE_NOMEM;
  4143  	}
  4144  	/* Zero the structure */
  4145  	SyZero(pVm,sizeof(unqlite_vm));
  4146  	/* Initialize */
  4147  	SyMemBackendInitFromParent(&pVm->sAlloc,&pDb->sMem);
  4148  	/* Allocate a new collection table */
  4149  	pVm->apCol = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc,32 * sizeof(unqlite_col *)); 
  4150  	if( pVm->apCol == 0 ){
  4151  		goto fail;
  4152  	}
  4153  	pVm->iColSize = 32; /* Must be a power of two */
  4154  	/* Zero the table */
  4155  	SyZero((void *)pVm->apCol,pVm->iColSize * sizeof(unqlite_col *));
  4156  #if defined(UNQLITE_ENABLE_THREADS)
  4157  	if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
  4158  		 /* Associate a recursive mutex with this instance */
  4159  		 pVm->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  4160  		 if( pVm->pMutex == 0 ){
  4161  			 goto fail;
  4162  		 }
  4163  	 }
  4164  #endif
  4165  	/* Link the VM to the list of active virtual machines */
  4166  	pVm->pJx9Vm = pJx9Vm;
  4167  	pVm->pDb = pDb;
  4168  	MACRO_LD_PUSH(pDb->pVms,pVm);
  4169  	pDb->iVm++;
  4170  	/* Register Jx9 functions */
  4171  	unqliteRegisterJx9Functions(pVm);
  4172  	/* Set the magic number */
  4173  	pVm->nMagic = JX9_VM_INIT; /* Same magic number as Jx9 */
  4174  	/* All done */
  4175  	*ppOut = pVm;
  4176  	return UNQLITE_OK;
  4177  fail:
  4178  	SyMemBackendRelease(&pVm->sAlloc);
  4179  	SyMemBackendPoolFree(&pDb->sMem,pVm);
  4180  	return UNQLITE_NOMEM;
  4181  }
  4182  /*
  4183   * Release an active VM.
  4184   */
  4185  static int unqliteVmRelease(unqlite_vm *pVm)
  4186  {
  4187  	/* Release the Jx9 VM */
  4188  	jx9_vm_release(pVm->pJx9Vm);
  4189  	/* Release the private memory backend */
  4190  	SyMemBackendRelease(&pVm->sAlloc);
  4191  	/* Upper layer will discard this VM from the list
  4192  	 * of active VM.
  4193  	 */
  4194  	return UNQLITE_OK;
  4195  }
  4196  /*
  4197   * Return the default page size.
  4198   */
  4199  UNQLITE_PRIVATE int unqliteGetPageSize(void)
  4200  {
  4201  	int iSize =  sUnqlMPGlobal.iPageSize;
  4202  	if( iSize < UNQLITE_MIN_PAGE_SIZE || iSize > UNQLITE_MAX_PAGE_SIZE ){
  4203  		iSize = UNQLITE_DEFAULT_PAGE_SIZE;
  4204  	}
  4205  	return iSize;
  4206  }
  4207  /*
  4208   * Generate an error message.
  4209   */
  4210  UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr)
  4211  {
  4212  	int rc;
  4213  	/* Append the error message */
  4214  	rc = SyBlobAppend(&pDb->sErr,(const void *)zErr,SyStrlen(zErr));
  4215  	/* Append a new line */
  4216  	SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
  4217  	return rc;
  4218  }
  4219  /*
  4220   * Generate an error message (Printf like).
  4221   */
  4222  UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...)
  4223  {
  4224  	va_list ap;
  4225  	int rc;
  4226  	va_start(ap,zFmt);
  4227  	rc = SyBlobFormatAp(&pDb->sErr,zFmt,ap);
  4228  	va_end(ap);
  4229  	/* Append a new line */
  4230  	SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
  4231  	return rc;
  4232  }
  4233  /*
  4234   * Generate an error message (Out of memory).
  4235   */
  4236  UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb)
  4237  {
  4238  	int rc;
  4239  	rc = unqliteGenError(pDb,"unQLite is running out of memory");
  4240  	return rc;
  4241  }
  4242  /*
  4243   * Configure a working UnQLite database handle.
  4244   */
  4245  static int unqliteConfigure(unqlite *pDb,int nOp,va_list ap)
  4246  {
  4247  	int rc = UNQLITE_OK;
  4248  	switch(nOp){
  4249  	case UNQLITE_CONFIG_JX9_ERR_LOG:
  4250  		/* Jx9 compile-time error log */
  4251  		rc = jx9EngineConfig(pDb->sDB.pJx9,JX9_CONFIG_ERR_LOG,ap);
  4252  		break;
  4253  	case UNQLITE_CONFIG_MAX_PAGE_CACHE: {
  4254  		int max_page = va_arg(ap,int);
  4255  		/* Maximum number of page to cache (Simple hint). */
  4256  		rc = unqlitePagerSetCachesize(pDb->sDB.pPager,max_page);
  4257  		break;
  4258  										}
  4259  	case UNQLITE_CONFIG_ERR_LOG: {
  4260  		/* Database error log if any */
  4261  		const char **pzPtr = va_arg(ap, const char **);
  4262  		int *pLen = va_arg(ap, int *);
  4263  		if( pzPtr == 0 ){
  4264  			rc = JX9_CORRUPT;
  4265  			break;
  4266  		}
  4267  		/* NULL terminate the error-log buffer */
  4268  		SyBlobNullAppend(&pDb->sErr);
  4269  		/* Point to the error-log buffer */
  4270  		*pzPtr = (const char *)SyBlobData(&pDb->sErr);
  4271  		if( pLen ){
  4272  			if( SyBlobLength(&pDb->sErr) > 1 /* NULL '\0' terminator */ ){
  4273  				*pLen = (int)SyBlobLength(&pDb->sErr);
  4274  			}else{
  4275  				*pLen = 0;
  4276  			}
  4277  		}
  4278  		break;
  4279  								 }
  4280  	case UNQLITE_CONFIG_DISABLE_AUTO_COMMIT:{
  4281  		/* Disable auto-commit */
  4282  		pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
  4283  		break;
  4284  											}
  4285  	case UNQLITE_CONFIG_GET_KV_NAME: {
  4286  		/* Name of the underlying KV storage engine */
  4287  		const char **pzPtr = va_arg(ap,const char **);
  4288  		if( pzPtr ){
  4289  			unqlite_kv_engine *pEngine;
  4290  			pEngine = unqlitePagerGetKvEngine(pDb);
  4291  			/* Point to the name */
  4292  			*pzPtr = pEngine->pIo->pMethods->zName;
  4293  		}
  4294  		break;
  4295  									 }
  4296  	default:
  4297  		/* Unknown configuration option */
  4298  		rc = UNQLITE_UNKNOWN;
  4299  		break;
  4300  	}
  4301  	return rc;
  4302  }
  4303  /*
  4304   * Export the global (master) memory allocator to submodules.
  4305   */
  4306  UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void)
  4307  {
  4308  	return &sUnqlMPGlobal.sAllocator;
  4309  }
  4310  /*
  4311   * [CAPIREF: unqlite_open()]
  4312   * Please refer to the official documentation for function purpose and expected parameters.
  4313   */
  4314  int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode)
  4315  {
  4316  	unqlite *pHandle;
  4317  	int rc;
  4318  #if defined(UNTRUST)
  4319  	if( ppDB == 0 ){
  4320  		return UNQLITE_CORRUPT;
  4321  	}
  4322  #endif
  4323  	*ppDB = 0;
  4324  	/* One-time automatic library initialization */
  4325  	rc = unqliteCoreInitialize();
  4326  	if( rc != UNQLITE_OK ){
  4327  		return rc;
  4328  	}
  4329  	/* Allocate a new database handle */
  4330  	pHandle = (unqlite *)SyMemBackendPoolAlloc(&sUnqlMPGlobal.sAllocator, sizeof(unqlite));
  4331  	if( pHandle == 0 ){
  4332  		return UNQLITE_NOMEM;
  4333  	}
  4334  	/* Zero the structure */
  4335  	SyZero(pHandle,sizeof(unqlite));
  4336  	if( iMode < 1 ){
  4337  		/* Assume a read-only database */
  4338  		iMode = UNQLITE_OPEN_READONLY|UNQLITE_OPEN_MMAP;
  4339  	}
  4340  	/* Init the database */
  4341  	rc = unqliteInitDatabase(pHandle,&sUnqlMPGlobal.sAllocator,zFilename,iMode);
  4342  	if( rc != UNQLITE_OK ){
  4343  		goto Release;
  4344  	}
  4345  #if defined(UNQLITE_ENABLE_THREADS)
  4346  	if( !(iMode & UNQLITE_OPEN_NOMUTEX) && (sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE) ){
  4347  		 /* Associate a recursive mutex with this instance */
  4348  		 pHandle->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  4349  		 if( pHandle->pMutex == 0 ){
  4350  			 rc = UNQLITE_NOMEM;
  4351  			 goto Release;
  4352  		 }
  4353  	 }
  4354  #endif
  4355  	/* Link to the list of active DB handles */
  4356  #if defined(UNQLITE_ENABLE_THREADS)
  4357  	/* Enter the global mutex */
  4358  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  4359  #endif
  4360  	 MACRO_LD_PUSH(sUnqlMPGlobal.pDB,pHandle);
  4361  	 sUnqlMPGlobal.nDB++;
  4362  #if defined(UNQLITE_ENABLE_THREADS)
  4363  	/* Leave the global mutex */
  4364  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  4365  #endif
  4366  	/* Set the magic number to identify a valid DB handle */
  4367  	 pHandle->nMagic = UNQLITE_DB_MAGIC;
  4368  	/* Make the handle available to the caller */
  4369  	*ppDB = pHandle;
  4370  	return UNQLITE_OK;
  4371  Release:
  4372  	SyMemBackendRelease(&pHandle->sMem);
  4373  	SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pHandle);
  4374  	return rc;
  4375  }
  4376  /*
  4377   * [CAPIREF: unqlite_config()]
  4378   * Please refer to the official documentation for function purpose and expected parameters.
  4379   */
  4380  int unqlite_config(unqlite *pDb,int nConfigOp,...)
  4381  {
  4382  	va_list ap;
  4383  	int rc;
  4384  	if( UNQLITE_DB_MISUSE(pDb) ){
  4385  		return UNQLITE_CORRUPT;
  4386  	}
  4387  #if defined(UNQLITE_ENABLE_THREADS)
  4388  	 /* Acquire DB mutex */
  4389  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4390  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4391  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  4392  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4393  	 }
  4394  #endif
  4395  	 va_start(ap, nConfigOp);
  4396  	 rc = unqliteConfigure(&(*pDb),nConfigOp, ap);
  4397  	 va_end(ap);
  4398  #if defined(UNQLITE_ENABLE_THREADS)
  4399  	 /* Leave DB mutex */
  4400  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4401  #endif
  4402  	return rc;
  4403  }
  4404  /*
  4405   * [CAPIREF: unqlite_close()]
  4406   * Please refer to the official documentation for function purpose and expected parameters.
  4407   */
  4408  int unqlite_close(unqlite *pDb)
  4409  {
  4410  	int rc;
  4411  	if( UNQLITE_DB_MISUSE(pDb) ){
  4412  		return UNQLITE_CORRUPT;
  4413  	}
  4414  #if defined(UNQLITE_ENABLE_THREADS)
  4415  	 /* Acquire DB mutex */
  4416  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4417  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4418  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  4419  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4420  	 }
  4421  #endif
  4422  	/* Release the database handle */
  4423  	rc = unqliteDbRelease(pDb);
  4424  #if defined(UNQLITE_ENABLE_THREADS)
  4425  	 /* Leave DB mutex */
  4426  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4427  	 /* Release DB mutex */
  4428  	 SyMutexRelease(sUnqlMPGlobal.pMutexMethods, pDb->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4429  #endif
  4430  #if defined(UNQLITE_ENABLE_THREADS)
  4431  	/* Enter the global mutex */
  4432  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  4433  #endif
  4434  	/* Unlink from the list of active database handles */
  4435  	 MACRO_LD_REMOVE(sUnqlMPGlobal.pDB, pDb);
  4436  	sUnqlMPGlobal.nDB--;
  4437  #if defined(UNQLITE_ENABLE_THREADS)
  4438  	/* Leave the global mutex */
  4439  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
  4440  #endif
  4441  	/* Release the memory chunk allocated to this handle */
  4442  	SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pDb);
  4443  	return rc;
  4444  }
  4445  /*
  4446   * [CAPIREF: unqlite_compile()]
  4447   * Please refer to the official documentation for function purpose and expected parameters.
  4448   */
  4449  int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut)
  4450  {
  4451  	jx9_vm *pVm;
  4452  	int rc;
  4453  	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
  4454  		return UNQLITE_CORRUPT;
  4455  	}
  4456  #if defined(UNQLITE_ENABLE_THREADS)
  4457  	 /* Acquire DB mutex */
  4458  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4459  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4460  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  4461  			 return UNQLITE_ABORT;
  4462  	 }
  4463  #endif
  4464  	 /* Compile the Jx9 script first */
  4465  	 rc = jx9_compile(pDb->sDB.pJx9,zJx9,nByte,&pVm);
  4466  	 if( rc == JX9_OK ){
  4467  		 /* Allocate a new unqlite VM instance */
  4468  		 rc = unqliteInitVm(pDb,pVm,ppOut);
  4469  		 if( rc != UNQLITE_OK ){
  4470  			 /* Release the Jx9 VM */
  4471  			 jx9_vm_release(pVm);
  4472  		 }
  4473  	 }
  4474  #if defined(UNQLITE_ENABLE_THREADS)
  4475  	 /* Leave DB mutex */
  4476  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4477  #endif
  4478  	return rc;
  4479  }
  4480  /*
  4481   * [CAPIREF: unqlite_compile_file()]
  4482   * Please refer to the official documentation for function purpose and expected parameters.
  4483   */
  4484  int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut)
  4485  {
  4486  	jx9_vm *pVm;
  4487  	int rc;
  4488  	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
  4489  		return UNQLITE_CORRUPT;
  4490  	}
  4491  #if defined(UNQLITE_ENABLE_THREADS)
  4492  	 /* Acquire DB mutex */
  4493  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4494  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4495  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  4496  			 return UNQLITE_ABORT;
  4497  	 }
  4498  #endif
  4499  	 /* Compile the Jx9 script first */
  4500  	rc = jx9_compile_file(pDb->sDB.pJx9,zPath,&pVm);
  4501  	if( rc == JX9_OK ){
  4502  		/* Allocate a new unqlite VM instance */
  4503  		rc = unqliteInitVm(pDb,pVm,ppOut);
  4504  		if( rc != UNQLITE_OK ){
  4505  			/* Release the Jx9 VM */
  4506  			jx9_vm_release(pVm);
  4507  		}
  4508  	}
  4509  #if defined(UNQLITE_ENABLE_THREADS)
  4510  	 /* Leave DB mutex */
  4511  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4512  #endif
  4513  	return rc;
  4514  }
  4515  /*
  4516   * Configure an unqlite virtual machine (Mostly Jx9 VM) instance.
  4517   */
  4518  static int unqliteVmConfig(unqlite_vm *pVm,sxi32 iOp,va_list ap)
  4519  {
  4520  	int rc;
  4521  	rc = jx9VmConfigure(pVm->pJx9Vm,iOp,ap);
  4522  	return rc;
  4523  }
  4524  /*
  4525   * [CAPIREF: unqlite_vm_config()]
  4526   * Please refer to the official documentation for function purpose and expected parameters.
  4527   */
  4528  int unqlite_vm_config(unqlite_vm *pVm,int iOp,...)
  4529  {
  4530  	va_list ap;
  4531  	int rc;
  4532  	if( UNQLITE_VM_MISUSE(pVm) ){
  4533  		return UNQLITE_CORRUPT;
  4534  	}
  4535  #if defined(UNQLITE_ENABLE_THREADS)
  4536  	 /* Acquire VM mutex */
  4537  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4538  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4539  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4540  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4541  	 }
  4542  #endif
  4543  	 va_start(ap,iOp);
  4544  	 rc = unqliteVmConfig(pVm,iOp,ap);
  4545  	 va_end(ap);
  4546  #if defined(UNQLITE_ENABLE_THREADS)
  4547  	 /* Leave DB mutex */
  4548  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4549  #endif
  4550  	return rc;
  4551  }
  4552  /*
  4553   * [CAPIREF: unqlite_vm_exec()]
  4554   * Please refer to the official documentation for function purpose and expected parameters.
  4555   */
  4556  int unqlite_vm_exec(unqlite_vm *pVm)
  4557  {
  4558  	int rc;
  4559  	if( UNQLITE_VM_MISUSE(pVm) ){
  4560  		return UNQLITE_CORRUPT;
  4561  	}
  4562  #if defined(UNQLITE_ENABLE_THREADS)
  4563  	 /* Acquire VM mutex */
  4564  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4565  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4566  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4567  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4568  	 }
  4569  #endif
  4570  	/* Execute the Jx9 bytecode program */
  4571  	 rc = jx9VmByteCodeExec(pVm->pJx9Vm);
  4572  #if defined(UNQLITE_ENABLE_THREADS)
  4573  	 /* Leave DB mutex */
  4574  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4575  #endif
  4576  	return rc;
  4577  }
  4578  /*
  4579   * [CAPIREF: unqlite_vm_release()]
  4580   * Please refer to the official documentation for function purpose and expected parameters.
  4581   */
  4582  int unqlite_vm_release(unqlite_vm *pVm)
  4583  {
  4584  	int rc;
  4585  	if( UNQLITE_VM_MISUSE(pVm) ){
  4586  		return UNQLITE_CORRUPT;
  4587  	}
  4588  #if defined(UNQLITE_ENABLE_THREADS)
  4589  	 /* Acquire VM mutex */
  4590  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4591  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4592  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4593  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4594  	 }
  4595  #endif
  4596  	/* Release the VM */
  4597  	 rc = unqliteVmRelease(pVm);
  4598  #if defined(UNQLITE_ENABLE_THREADS)
  4599  	 /* Leave VM mutex */
  4600  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4601  	 /* Release VM mutex */
  4602  	 SyMutexRelease(sUnqlMPGlobal.pMutexMethods,pVm->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4603  #endif
  4604  	 if( rc == UNQLITE_OK ){
  4605  		 unqlite *pDb = pVm->pDb;
  4606  		 /* Unlink from the list of active VM's */
  4607  #if defined(UNQLITE_ENABLE_THREADS)
  4608  			/* Acquire DB mutex */
  4609  			SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4610  			if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4611  				UNQLITE_THRD_DB_RELEASE(pDb) ){
  4612  					return UNQLITE_ABORT; /* Another thread have released this instance */
  4613  			}
  4614  #endif
  4615  		MACRO_LD_REMOVE(pDb->pVms, pVm);
  4616  		pDb->iVm--;
  4617  		/* Release the memory chunk allocated to this instance */
  4618  		SyMemBackendPoolFree(&pDb->sMem,pVm);
  4619  #if defined(UNQLITE_ENABLE_THREADS)
  4620  			/* Leave DB mutex */
  4621  			SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4622  #endif
  4623  	 }
  4624  	 return rc;
  4625  }
  4626  /*
  4627   * [CAPIREF: unqlite_vm_reset()]
  4628   * Please refer to the official documentation for function purpose and expected parameters.
  4629   */
  4630  int unqlite_vm_reset(unqlite_vm *pVm)
  4631  {
  4632  	int rc;
  4633  	if( UNQLITE_VM_MISUSE(pVm) ){
  4634  		return UNQLITE_CORRUPT;
  4635  	}
  4636  #if defined(UNQLITE_ENABLE_THREADS)
  4637  	 /* Acquire VM mutex */
  4638  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4639  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4640  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4641  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4642  	 }
  4643  #endif
  4644  	/* Reset the Jx9 VM */
  4645  	 rc = jx9VmReset(pVm->pJx9Vm);
  4646  #if defined(UNQLITE_ENABLE_THREADS)
  4647  	 /* Leave DB mutex */
  4648  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4649  #endif
  4650  	return rc;
  4651  }
  4652  /*
  4653   * [CAPIREF: unqlite_vm_dump()]
  4654   * Please refer to the official documentation for function purpose and expected parameters.
  4655   */
  4656  int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData)
  4657  {
  4658  	int rc;
  4659  	if( UNQLITE_VM_MISUSE(pVm) ){
  4660  		return UNQLITE_CORRUPT;
  4661  	}
  4662  #if defined(UNQLITE_ENABLE_THREADS)
  4663  	 /* Acquire VM mutex */
  4664  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4665  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4666  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4667  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4668  	 }
  4669  #endif
  4670  	/* Dump the Jx9 VM */
  4671  	 rc = jx9VmDump(pVm->pJx9Vm,xConsumer,pUserData);
  4672  #if defined(UNQLITE_ENABLE_THREADS)
  4673  	 /* Leave DB mutex */
  4674  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4675  #endif
  4676  	return rc;
  4677  }
  4678  /*
  4679   * [CAPIREF: unqlite_vm_extract_variable()]
  4680   * Please refer to the official documentation for function purpose and expected parameters.
  4681   */
  4682  unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname)
  4683  {
  4684  	unqlite_value *pValue;
  4685  	SyString sVariable;
  4686  	if( UNQLITE_VM_MISUSE(pVm) ){
  4687  		return 0;
  4688  	}
  4689  #if defined(UNQLITE_ENABLE_THREADS)
  4690  	 /* Acquire VM mutex */
  4691  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4692  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4693  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4694  			 return 0; /* Another thread have released this instance */
  4695  	 }
  4696  #endif
  4697  	 /* Extract the target variable */
  4698  	SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname));
  4699  	pValue = jx9VmExtractVariable(pVm->pJx9Vm,&sVariable);
  4700  #if defined(UNQLITE_ENABLE_THREADS)
  4701  	 /* Leave DB mutex */
  4702  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4703  #endif
  4704  	return pValue;
  4705  }
  4706  /*
  4707   * [CAPIREF: unqlite_create_function()]
  4708   * Please refer to the official documentation for function purpose and expected parameters.
  4709   */
  4710  int unqlite_create_function(unqlite_vm *pVm, const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData)
  4711  {
  4712  	SyString sName;
  4713  	int rc;
  4714  	if( UNQLITE_VM_MISUSE(pVm) ){
  4715  		return UNQLITE_CORRUPT;
  4716  	}
  4717  	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  4718  	/* Remove leading and trailing white spaces */
  4719  	SyStringFullTrim(&sName);
  4720  	/* Ticket 1433-003: NULL values are not allowed */
  4721  	if( sName.nByte < 1 || xFunc == 0 ){
  4722  		return UNQLITE_INVALID;
  4723  	}
  4724  #if defined(UNQLITE_ENABLE_THREADS)
  4725  	 /* Acquire VM mutex */
  4726  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4727  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4728  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4729  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4730  	 }
  4731  #endif
  4732  	 /* Install the foreign function */
  4733  	 rc = jx9VmInstallForeignFunction(pVm->pJx9Vm,&sName,xFunc,pUserData);
  4734  #if defined(UNQLITE_ENABLE_THREADS)
  4735  	 /* Leave DB mutex */
  4736  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4737  #endif
  4738  	return rc;
  4739  }
  4740  /*
  4741   * [CAPIREF: unqlite_delete_function()]
  4742   * Please refer to the official documentation for function purpose and expected parameters.
  4743   */
  4744  int unqlite_delete_function(unqlite_vm *pVm, const char *zName)
  4745  {
  4746  	int rc;
  4747  	if( UNQLITE_VM_MISUSE(pVm) ){
  4748  		return UNQLITE_CORRUPT;
  4749  	}
  4750  #if defined(UNQLITE_ENABLE_THREADS)
  4751  	 /* Acquire VM mutex */
  4752  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4753  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4754  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4755  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4756  	 }
  4757  #endif
  4758  	 /* Unlink the foreign function */
  4759  	 rc = jx9DeleteFunction(pVm->pJx9Vm,zName);
  4760  #if defined(UNQLITE_ENABLE_THREADS)
  4761  	 /* Leave DB mutex */
  4762  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4763  #endif
  4764  	return rc;
  4765  }
  4766  /*
  4767   * [CAPIREF: unqlite_create_constant()]
  4768   * Please refer to the official documentation for function purpose and expected parameters.
  4769   */
  4770  int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData)
  4771  {
  4772  	SyString sName;
  4773  	int rc;
  4774  	if( UNQLITE_VM_MISUSE(pVm) ){
  4775  		return UNQLITE_CORRUPT;
  4776  	}
  4777  	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  4778  	/* Remove leading and trailing white spaces */
  4779  	SyStringFullTrim(&sName);
  4780  	if( sName.nByte < 1 ){
  4781  		/* Empty constant name */
  4782  		return UNQLITE_INVALID;
  4783  	}
  4784  	/* TICKET 1433-003: NULL pointer is harmless operation */
  4785  	if( xExpand == 0 ){
  4786  		return UNQLITE_INVALID;
  4787  	}
  4788  #if defined(UNQLITE_ENABLE_THREADS)
  4789  	 /* Acquire VM mutex */
  4790  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4791  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4792  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4793  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4794  	 }
  4795  #endif
  4796  	 /* Install the foreign constant */
  4797  	 rc = jx9VmRegisterConstant(pVm->pJx9Vm,&sName,xExpand,pUserData);
  4798  #if defined(UNQLITE_ENABLE_THREADS)
  4799  	 /* Leave DB mutex */
  4800  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4801  #endif
  4802  	return rc;
  4803  }
  4804  /*
  4805   * [CAPIREF: unqlite_delete_constant()]
  4806   * Please refer to the official documentation for function purpose and expected parameters.
  4807   */
  4808  int unqlite_delete_constant(unqlite_vm *pVm, const char *zName)
  4809  {
  4810  	int rc;
  4811  	if( UNQLITE_VM_MISUSE(pVm) ){
  4812  		return UNQLITE_CORRUPT;
  4813  	}
  4814  #if defined(UNQLITE_ENABLE_THREADS)
  4815  	 /* Acquire VM mutex */
  4816  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4817  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  4818  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  4819  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  4820  	 }
  4821  #endif
  4822  	 /* Unlink the foreign constant */
  4823  	 rc = Jx9DeleteConstant(pVm->pJx9Vm,zName);
  4824  #if defined(UNQLITE_ENABLE_THREADS)
  4825  	 /* Leave DB mutex */
  4826  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  4827  #endif
  4828  	return rc;
  4829  }
  4830  /*
  4831   * [CAPIREF: unqlite_value_int()]
  4832   * Please refer to the official documentation for function purpose and expected parameters.
  4833   */
  4834  int unqlite_value_int(unqlite_value *pVal, int iValue)
  4835  {
  4836  	return jx9_value_int(pVal,iValue);
  4837  }
  4838  /*
  4839   * [CAPIREF: unqlite_value_int64()]
  4840   * Please refer to the official documentation for function purpose and expected parameters.
  4841   */
  4842  int unqlite_value_int64(unqlite_value *pVal,unqlite_int64 iValue)
  4843  {
  4844  	return jx9_value_int64(pVal,iValue);
  4845  }
  4846  /*
  4847   * [CAPIREF: unqlite_value_bool()]
  4848   * Please refer to the official documentation for function purpose and expected parameters.
  4849   */
  4850  int unqlite_value_bool(unqlite_value *pVal, int iBool)
  4851  {
  4852  	return jx9_value_bool(pVal,iBool);
  4853  }
  4854  /*
  4855   * [CAPIREF: unqlite_value_null()]
  4856   * Please refer to the official documentation for function purpose and expected parameters.
  4857   */
  4858  int unqlite_value_null(unqlite_value *pVal)
  4859  {
  4860  	return jx9_value_null(pVal);
  4861  }
  4862  /*
  4863   * [CAPIREF: unqlite_value_double()]
  4864   * Please refer to the official documentation for function purpose and expected parameters.
  4865   */
  4866  int unqlite_value_double(unqlite_value *pVal, double Value)
  4867  {
  4868  	return jx9_value_double(pVal,Value);
  4869  }
  4870  /*
  4871   * [CAPIREF: unqlite_value_string()]
  4872   * Please refer to the official documentation for function purpose and expected parameters.
  4873   */
  4874  int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen)
  4875  {
  4876  	return jx9_value_string(pVal,zString,nLen);
  4877  }
  4878  /*
  4879   * [CAPIREF: unqlite_value_string_format()]
  4880   * Please refer to the official documentation for function purpose and expected parameters.
  4881   */
  4882  int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...)
  4883  {
  4884  	va_list ap;
  4885  	int rc;
  4886  	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
  4887  		/* Invalidate any prior representation */
  4888  		jx9MemObjRelease(pVal);
  4889  		MemObjSetType(pVal, MEMOBJ_STRING);
  4890  	}
  4891  	va_start(ap, zFormat);
  4892  	rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
  4893  	va_end(ap);
  4894  	return UNQLITE_OK;
  4895  }
  4896  /*
  4897   * [CAPIREF: unqlite_value_reset_string_cursor()]
  4898   * Please refer to the official documentation for function purpose and expected parameters.
  4899   */
  4900  int unqlite_value_reset_string_cursor(unqlite_value *pVal)
  4901  {
  4902  	return jx9_value_reset_string_cursor(pVal);
  4903  }
  4904  /*
  4905   * [CAPIREF: unqlite_value_resource()]
  4906   * Please refer to the official documentation for function purpose and expected parameters.
  4907   */
  4908  int unqlite_value_resource(unqlite_value *pVal,void *pUserData)
  4909  {
  4910  	return jx9_value_resource(pVal,pUserData);
  4911  }
  4912  /*
  4913   * [CAPIREF: unqlite_value_release()]
  4914   * Please refer to the official documentation for function purpose and expected parameters.
  4915   */
  4916  int unqlite_value_release(unqlite_value *pVal)
  4917  {
  4918  	return jx9_value_release(pVal);
  4919  }
  4920  /*
  4921   * [CAPIREF: unqlite_value_to_int()]
  4922   * Please refer to the official documentation for function purpose and expected parameters.
  4923   */
  4924  int unqlite_value_to_int(unqlite_value *pValue)
  4925  {
  4926  	return jx9_value_to_int(pValue);
  4927  }
  4928  /*
  4929   * [CAPIREF: unqlite_value_to_bool()]
  4930   * Please refer to the official documentation for function purpose and expected parameters.
  4931   */
  4932  int unqlite_value_to_bool(unqlite_value *pValue)
  4933  {
  4934  	return jx9_value_to_bool(pValue);
  4935  }
  4936  /*
  4937   * [CAPIREF: unqlite_value_to_int64()]
  4938   * Please refer to the official documentation for function purpose and expected parameters.
  4939   */
  4940  unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue)
  4941  {
  4942  	return jx9_value_to_int64(pValue);
  4943  }
  4944  /*
  4945   * [CAPIREF: unqlite_value_to_double()]
  4946   * Please refer to the official documentation for function purpose and expected parameters.
  4947   */
  4948  double unqlite_value_to_double(unqlite_value *pValue)
  4949  {
  4950  	return jx9_value_to_double(pValue);
  4951  }
  4952  /*
  4953   * [CAPIREF: unqlite_value_to_string()]
  4954   * Please refer to the official documentation for function purpose and expected parameters.
  4955   */
  4956  const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen)
  4957  {
  4958  	return jx9_value_to_string(pValue,pLen);
  4959  }
  4960  /*
  4961   * [CAPIREF: unqlite_value_to_resource()]
  4962   * Please refer to the official documentation for function purpose and expected parameters.
  4963   */
  4964  void * unqlite_value_to_resource(unqlite_value *pValue)
  4965  {
  4966  	return jx9_value_to_resource(pValue);
  4967  }
  4968  /*
  4969   * [CAPIREF: unqlite_value_compare()]
  4970   * Please refer to the official documentation for function purpose and expected parameters.
  4971   */
  4972  int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict)
  4973  {
  4974  	return jx9_value_compare(pLeft,pRight,bStrict);
  4975  }
  4976  /*
  4977   * [CAPIREF: unqlite_result_int()]
  4978   * Please refer to the official documentation for function purpose and expected parameters.
  4979   */
  4980  int unqlite_result_int(unqlite_context *pCtx, int iValue)
  4981  {
  4982  	return jx9_result_int(pCtx,iValue);
  4983  }
  4984  /*
  4985   * [CAPIREF: unqlite_result_int64()]
  4986   * Please refer to the official documentation for function purpose and expected parameters.
  4987   */
  4988  int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue)
  4989  {
  4990  	return jx9_result_int64(pCtx,iValue);
  4991  }
  4992  /*
  4993   * [CAPIREF: unqlite_result_bool()]
  4994   * Please refer to the official documentation for function purpose and expected parameters.
  4995   */
  4996  int unqlite_result_bool(unqlite_context *pCtx, int iBool)
  4997  {
  4998  	return jx9_result_bool(pCtx,iBool);
  4999  }
  5000  /*
  5001   * [CAPIREF: unqlite_result_double()]
  5002   * Please refer to the official documentation for function purpose and expected parameters.
  5003   */
  5004  int unqlite_result_double(unqlite_context *pCtx, double Value)
  5005  {
  5006  	return jx9_result_double(pCtx,Value);
  5007  }
  5008  /*
  5009   * [CAPIREF: unqlite_result_null()]
  5010   * Please refer to the official documentation for function purpose and expected parameters.
  5011   */
  5012  int unqlite_result_null(unqlite_context *pCtx)
  5013  {
  5014  	return jx9_result_null(pCtx);
  5015  }
  5016  /*
  5017   * [CAPIREF: unqlite_result_string()]
  5018   * Please refer to the official documentation for function purpose and expected parameters.
  5019   */
  5020  int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen)
  5021  {
  5022  	return jx9_result_string(pCtx,zString,nLen);
  5023  }
  5024  /*
  5025   * [CAPIREF: unqlite_result_string_format()]
  5026   * Please refer to the official documentation for function purpose and expected parameters.
  5027   */
  5028  int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...)
  5029  {
  5030  	jx9_value *p;
  5031  	va_list ap;
  5032  	int rc;
  5033  	p = pCtx->pRet;
  5034  	if( (p->iFlags & MEMOBJ_STRING) == 0 ){
  5035  		/* Invalidate any prior representation */
  5036  		jx9MemObjRelease(p);
  5037  		MemObjSetType(p, MEMOBJ_STRING);
  5038  	}
  5039  	/* Format the given string */
  5040  	va_start(ap, zFormat);
  5041  	rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
  5042  	va_end(ap);
  5043  	return rc;
  5044  }
  5045  /*
  5046   * [CAPIREF: unqlite_result_value()]
  5047   * Please refer to the official documentation for function purpose and expected parameters.
  5048   */
  5049  int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue)
  5050  {
  5051  	return jx9_result_value(pCtx,pValue);
  5052  }
  5053  /*
  5054   * [CAPIREF: unqlite_result_resource()]
  5055   * Please refer to the official documentation for function purpose and expected parameters.
  5056   */
  5057  int unqlite_result_resource(unqlite_context *pCtx, void *pUserData)
  5058  {
  5059  	return jx9_result_resource(pCtx,pUserData);
  5060  }
  5061  /*
  5062   * [CAPIREF: unqlite_value_is_int()]
  5063   * Please refer to the official documentation for function purpose and expected parameters.
  5064   */
  5065  int unqlite_value_is_int(unqlite_value *pVal)
  5066  {
  5067  	return jx9_value_is_int(pVal);
  5068  }
  5069  /*
  5070   * [CAPIREF: unqlite_value_is_float()]
  5071   * Please refer to the official documentation for function purpose and expected parameters.
  5072   */
  5073  int unqlite_value_is_float(unqlite_value *pVal)
  5074  {
  5075  	return jx9_value_is_float(pVal);
  5076  }
  5077  /*
  5078   * [CAPIREF: unqlite_value_is_bool()]
  5079   * Please refer to the official documentation for function purpose and expected parameters.
  5080   */
  5081  int unqlite_value_is_bool(unqlite_value *pVal)
  5082  {
  5083  	return jx9_value_is_bool(pVal);
  5084  }
  5085  /*
  5086   * [CAPIREF: unqlite_value_is_string()]
  5087   * Please refer to the official documentation for function purpose and expected parameters.
  5088   */
  5089  int unqlite_value_is_string(unqlite_value *pVal)
  5090  {
  5091  	return jx9_value_is_string(pVal);
  5092  }
  5093  /*
  5094   * [CAPIREF: unqlite_value_is_null()]
  5095   * Please refer to the official documentation for function purpose and expected parameters.
  5096   */
  5097  int unqlite_value_is_null(unqlite_value *pVal)
  5098  {
  5099  	return jx9_value_is_null(pVal);
  5100  }
  5101  /*
  5102   * [CAPIREF: unqlite_value_is_numeric()]
  5103   * Please refer to the official documentation for function purpose and expected parameters.
  5104   */
  5105  int unqlite_value_is_numeric(unqlite_value *pVal)
  5106  {
  5107  	return jx9_value_is_numeric(pVal);
  5108  }
  5109  /*
  5110   * [CAPIREF: unqlite_value_is_callable()]
  5111   * Please refer to the official documentation for function purpose and expected parameters.
  5112   */
  5113  int unqlite_value_is_callable(unqlite_value *pVal)
  5114  {
  5115  	return jx9_value_is_callable(pVal);
  5116  }
  5117  /*
  5118   * [CAPIREF: unqlite_value_is_scalar()]
  5119   * Please refer to the official documentation for function purpose and expected parameters.
  5120   */
  5121  int unqlite_value_is_scalar(unqlite_value *pVal)
  5122  {
  5123  	return jx9_value_is_scalar(pVal);
  5124  }
  5125  /*
  5126   * [CAPIREF: unqlite_value_is_json_array()]
  5127   * Please refer to the official documentation for function purpose and expected parameters.
  5128   */
  5129  int unqlite_value_is_json_array(unqlite_value *pVal)
  5130  {
  5131  	return jx9_value_is_json_array(pVal);
  5132  }
  5133  /*
  5134   * [CAPIREF: unqlite_value_is_json_object()]
  5135   * Please refer to the official documentation for function purpose and expected parameters.
  5136   */
  5137  int unqlite_value_is_json_object(unqlite_value *pVal)
  5138  {
  5139  	return jx9_value_is_json_object(pVal);
  5140  }
  5141  /*
  5142   * [CAPIREF: unqlite_value_is_resource()]
  5143   * Please refer to the official documentation for function purpose and expected parameters.
  5144   */
  5145  int unqlite_value_is_resource(unqlite_value *pVal)
  5146  {
  5147  	return jx9_value_is_resource(pVal);
  5148  }
  5149  /*
  5150   * [CAPIREF: unqlite_value_is_empty()]
  5151   * Please refer to the official documentation for function purpose and expected parameters.
  5152   */
  5153  int unqlite_value_is_empty(unqlite_value *pVal)
  5154  {
  5155  	return jx9_value_is_empty(pVal);
  5156  }
  5157  /*
  5158   * [CAPIREF: unqlite_array_fetch()]
  5159   * Please refer to the official documentation for function purpose and expected parameters.
  5160   */
  5161  unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte)
  5162  {
  5163  	return jx9_array_fetch(pArray,zKey,nByte);
  5164  }
  5165  /*
  5166   * [CAPIREF: unqlite_array_walk()]
  5167   * Please refer to the official documentation for function purpose and expected parameters.
  5168   */
  5169  int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData)
  5170  {
  5171  	return jx9_array_walk(pArray,xWalk,pUserData);
  5172  }
  5173  /*
  5174   * [CAPIREF: unqlite_array_add_elem()]
  5175   * Please refer to the official documentation for function purpose and expected parameters.
  5176   */
  5177  int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue)
  5178  {
  5179  	return jx9_array_add_elem(pArray,pKey,pValue);
  5180  }
  5181  /*
  5182   * [CAPIREF: unqlite_array_add_strkey_elem()]
  5183   * Please refer to the official documentation for function purpose and expected parameters.
  5184   */
  5185  int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue)
  5186  {
  5187  	return jx9_array_add_strkey_elem(pArray,zKey,pValue);
  5188  }
  5189  /*
  5190   * [CAPIREF: unqlite_array_count()]
  5191   * Please refer to the official documentation for function purpose and expected parameters.
  5192   */
  5193  int unqlite_array_count(unqlite_value *pArray)
  5194  {
  5195  	return (int)jx9_array_count(pArray);
  5196  }
  5197  /*
  5198   * [CAPIREF: unqlite_vm_new_scalar()]
  5199   * Please refer to the official documentation for function purpose and expected parameters.
  5200   */
  5201  unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm)
  5202  {
  5203  	unqlite_value *pValue;
  5204  	if( UNQLITE_VM_MISUSE(pVm) ){
  5205  		return 0;
  5206  	}
  5207  #if defined(UNQLITE_ENABLE_THREADS)
  5208  	 /* Acquire VM mutex */
  5209  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5210  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5211  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  5212  			 return 0; /* Another thread have released this instance */
  5213  	 }
  5214  #endif
  5215  	 pValue = jx9_new_scalar(pVm->pJx9Vm);
  5216  #if defined(UNQLITE_ENABLE_THREADS)
  5217  	 /* Leave DB mutex */
  5218  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5219  #endif
  5220  	return pValue;
  5221  }
  5222  /*
  5223   * [CAPIREF: unqlite_vm_new_array()]
  5224   * Please refer to the official documentation for function purpose and expected parameters.
  5225   */
  5226  unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm)
  5227  {
  5228  	unqlite_value *pValue;
  5229  	if( UNQLITE_VM_MISUSE(pVm) ){
  5230  		return 0;
  5231  	}
  5232  #if defined(UNQLITE_ENABLE_THREADS)
  5233  	 /* Acquire VM mutex */
  5234  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5235  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5236  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  5237  			 return 0; /* Another thread have released this instance */
  5238  	 }
  5239  #endif
  5240  	 pValue = jx9_new_array(pVm->pJx9Vm);
  5241  #if defined(UNQLITE_ENABLE_THREADS)
  5242  	 /* Leave DB mutex */
  5243  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5244  #endif
  5245  	return pValue;
  5246  }
  5247  /*
  5248   * [CAPIREF: unqlite_vm_release_value()]
  5249   * Please refer to the official documentation for function purpose and expected parameters.
  5250   */
  5251  int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue)
  5252  {
  5253  	int rc;
  5254  	if( UNQLITE_VM_MISUSE(pVm) ){
  5255  		return UNQLITE_CORRUPT;
  5256  	}
  5257  #if defined(UNQLITE_ENABLE_THREADS)
  5258  	 /* Acquire VM mutex */
  5259  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5260  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5261  		 UNQLITE_THRD_VM_RELEASE(pVm) ){
  5262  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5263  	 }
  5264  #endif
  5265  	 rc = jx9_release_value(pVm->pJx9Vm,pValue);
  5266  #if defined(UNQLITE_ENABLE_THREADS)
  5267  	 /* Leave DB mutex */
  5268  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5269  #endif
  5270  	return rc;
  5271  }
  5272  /*
  5273   * [CAPIREF: unqlite_context_output()]
  5274   * Please refer to the official documentation for function purpose and expected parameters.
  5275   */
  5276  int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen)
  5277  {
  5278  	return jx9_context_output(pCtx,zString,nLen);
  5279  }
  5280  /*
  5281   * [CAPIREF: unqlite_context_output_format()]
  5282   * Please refer to the official documentation for function purpose and expected parameters.
  5283   */
  5284  int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...)
  5285  {
  5286  	va_list ap;
  5287  	int rc;
  5288  	va_start(ap, zFormat);
  5289  	rc = jx9VmOutputConsumeAp(pCtx->pVm,zFormat, ap);
  5290  	va_end(ap);
  5291  	return rc;
  5292  }
  5293  /*
  5294   * [CAPIREF: unqlite_context_throw_error()]
  5295   * Please refer to the official documentation for function purpose and expected parameters.
  5296   */
  5297  int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr)
  5298  {
  5299  	return jx9_context_throw_error(pCtx,iErr,zErr);
  5300  }
  5301  /*
  5302   * [CAPIREF: unqlite_context_throw_error_format()]
  5303   * Please refer to the official documentation for function purpose and expected parameters.
  5304   */
  5305  int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...)
  5306  {
  5307  	va_list ap;
  5308  	int rc;
  5309  	if( zFormat == 0){
  5310  		return JX9_OK;
  5311  	}
  5312  	va_start(ap, zFormat);
  5313  	rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
  5314  	va_end(ap);
  5315  	return rc;
  5316  }
  5317  /*
  5318   * [CAPIREF: unqlite_context_random_num()]
  5319   * Please refer to the official documentation for function purpose and expected parameters.
  5320   */
  5321  unsigned int unqlite_context_random_num(unqlite_context *pCtx)
  5322  {
  5323  	return jx9_context_random_num(pCtx);
  5324  }
  5325  /*
  5326   * [CAPIREF: unqlite_context_random_string()]
  5327   * Please refer to the official documentation for function purpose and expected parameters.
  5328   */
  5329  int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen)
  5330  {
  5331  	return jx9_context_random_string(pCtx,zBuf,nBuflen);
  5332  }
  5333  /*
  5334   * [CAPIREF: unqlite_context_user_data()]
  5335   * Please refer to the official documentation for function purpose and expected parameters.
  5336   */
  5337  void * unqlite_context_user_data(unqlite_context *pCtx)
  5338  {
  5339  	return jx9_context_user_data(pCtx);
  5340  }
  5341  /*
  5342   * [CAPIREF: unqlite_context_push_aux_data()]
  5343   * Please refer to the official documentation for function purpose and expected parameters.
  5344   */
  5345  int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData)
  5346  {
  5347  	return jx9_context_push_aux_data(pCtx,pUserData);
  5348  }
  5349  /*
  5350   * [CAPIREF: unqlite_context_peek_aux_data()]
  5351   * Please refer to the official documentation for function purpose and expected parameters.
  5352   */
  5353  void * unqlite_context_peek_aux_data(unqlite_context *pCtx)
  5354  {
  5355  	return jx9_context_peek_aux_data(pCtx);
  5356  }
  5357  /*
  5358   * [CAPIREF: unqlite_context_pop_aux_data()]
  5359   * Please refer to the official documentation for function purpose and expected parameters.
  5360   */
  5361  void * unqlite_context_pop_aux_data(unqlite_context *pCtx)
  5362  {
  5363  	return jx9_context_pop_aux_data(pCtx);
  5364  }
  5365  /*
  5366   * [CAPIREF: unqlite_context_result_buf_length()]
  5367   * Please refer to the official documentation for function purpose and expected parameters.
  5368   */
  5369  unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx)
  5370  {
  5371  	return jx9_context_result_buf_length(pCtx);
  5372  }
  5373  /*
  5374   * [CAPIREF: unqlite_function_name()]
  5375   * Please refer to the official documentation for function purpose and expected parameters.
  5376   */
  5377  const char * unqlite_function_name(unqlite_context *pCtx)
  5378  {
  5379  	return jx9_function_name(pCtx);
  5380  }
  5381  /*
  5382   * [CAPIREF: unqlite_context_new_scalar()]
  5383   * Please refer to the official documentation for function purpose and expected parameters.
  5384   */
  5385  unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx)
  5386  {
  5387  	return jx9_context_new_scalar(pCtx);
  5388  }
  5389  /*
  5390   * [CAPIREF: unqlite_context_new_array()]
  5391   * Please refer to the official documentation for function purpose and expected parameters.
  5392   */
  5393  unqlite_value * unqlite_context_new_array(unqlite_context *pCtx)
  5394  {
  5395  	return jx9_context_new_array(pCtx);
  5396  }
  5397  /*
  5398   * [CAPIREF: unqlite_context_release_value()]
  5399   * Please refer to the official documentation for function purpose and expected parameters.
  5400   */
  5401  void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue)
  5402  {
  5403  	jx9_context_release_value(pCtx,pValue);
  5404  }
  5405  /*
  5406   * [CAPIREF: unqlite_context_alloc_chunk()]
  5407   * Please refer to the official documentation for function purpose and expected parameters.
  5408   */
  5409  void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)
  5410  {
  5411  	return jx9_context_alloc_chunk(pCtx,nByte,ZeroChunk,AutoRelease);
  5412  }
  5413  /*
  5414   * [CAPIREF: unqlite_context_realloc_chunk()]
  5415   * Please refer to the official documentation for function purpose and expected parameters.
  5416   */
  5417  void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte)
  5418  {
  5419  	return jx9_context_realloc_chunk(pCtx,pChunk,nByte);
  5420  }
  5421  /*
  5422   * [CAPIREF: unqlite_context_free_chunk()]
  5423   * Please refer to the official documentation for function purpose and expected parameters.
  5424   */
  5425  void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk)
  5426  {
  5427  	jx9_context_free_chunk(pCtx,pChunk);
  5428  }
  5429  /*
  5430   * [CAPIREF: unqlite_kv_store()]
  5431   * Please refer to the official documentation for function purpose and expected parameters.
  5432   */
  5433  int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
  5434  {
  5435  	unqlite_kv_engine *pEngine;
  5436  	int rc;
  5437  	if( UNQLITE_DB_MISUSE(pDb) ){
  5438  		return UNQLITE_CORRUPT;
  5439  	}
  5440  #if defined(UNQLITE_ENABLE_THREADS)
  5441  	 /* Acquire DB mutex */
  5442  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5443  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5444  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5445  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5446  	 }
  5447  #endif
  5448  	 /* Point to the underlying storage engine */
  5449  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5450  	 if( pEngine->pIo->pMethods->xReplace == 0 ){
  5451  		 /* Storage engine does not implement such method */
  5452  		 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
  5453  		 rc = UNQLITE_NOTIMPLEMENTED;
  5454  	 }else{
  5455  		 if( nKeyLen < 0 ){
  5456  			 /* Assume a null terminated string and compute it's length */
  5457  			 nKeyLen = SyStrlen((const char *)pKey);
  5458  		 }
  5459  		 if( !nKeyLen ){
  5460  			 unqliteGenError(pDb,"Empty key");
  5461  			 rc = UNQLITE_EMPTY;
  5462  		 }else{
  5463  			 /* Perform the requested operation */
  5464  			 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,pData,nDataLen);
  5465  		 }
  5466  	 }
  5467  #if defined(UNQLITE_ENABLE_THREADS)
  5468  	 /* Leave DB mutex */
  5469  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5470  #endif
  5471  	return rc;
  5472  }
  5473  /*
  5474   * [CAPIREF: unqlite_kv_store_fmt()]
  5475   * Please refer to the official documentation for function purpose and expected parameters.
  5476   */
  5477  int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
  5478  {
  5479  	unqlite_kv_engine *pEngine;
  5480  	int rc;
  5481  	if( UNQLITE_DB_MISUSE(pDb) ){
  5482  		return UNQLITE_CORRUPT;
  5483  	}
  5484  #if defined(UNQLITE_ENABLE_THREADS)
  5485  	 /* Acquire DB mutex */
  5486  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5487  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5488  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5489  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5490  	 }
  5491  #endif
  5492  	 /* Point to the underlying storage engine */
  5493  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5494  	 if( pEngine->pIo->pMethods->xReplace == 0 ){
  5495  		 /* Storage engine does not implement such method */
  5496  		 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
  5497  		 rc = UNQLITE_NOTIMPLEMENTED;
  5498  	 }else{
  5499  		 if( nKeyLen < 0 ){
  5500  			 /* Assume a null terminated string and compute it's length */
  5501  			 nKeyLen = SyStrlen((const char *)pKey);
  5502  		 }
  5503  		 if( !nKeyLen ){
  5504  			 unqliteGenError(pDb,"Empty key");
  5505  			 rc = UNQLITE_EMPTY;
  5506  		 }else{
  5507  			 SyBlob sWorker; /* Working buffer */
  5508  			 va_list ap;
  5509  			 SyBlobInit(&sWorker,&pDb->sMem);
  5510  			 /* Format the data */
  5511  			 va_start(ap,zFormat);
  5512  			 SyBlobFormatAp(&sWorker,zFormat,ap);
  5513  			 va_end(ap);
  5514  			 /* Perform the requested operation */
  5515  			 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
  5516  			 /* Clean up */
  5517  			 SyBlobRelease(&sWorker);
  5518  		 }
  5519  	 }
  5520  #if defined(UNQLITE_ENABLE_THREADS)
  5521  	 /* Leave DB mutex */
  5522  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5523  #endif
  5524  	return rc;
  5525  }
  5526  /*
  5527   * [CAPIREF: unqlite_kv_append()]
  5528   * Please refer to the official documentation for function purpose and expected parameters.
  5529   */
  5530  int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
  5531  {
  5532  	unqlite_kv_engine *pEngine;
  5533  	int rc;
  5534  	if( UNQLITE_DB_MISUSE(pDb) ){
  5535  		return UNQLITE_CORRUPT;
  5536  	}
  5537  #if defined(UNQLITE_ENABLE_THREADS)
  5538  	 /* Acquire DB mutex */
  5539  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5540  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5541  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5542  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5543  	 }
  5544  #endif
  5545  	 /* Point to the underlying storage engine */
  5546  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5547  	 if( pEngine->pIo->pMethods->xAppend == 0 ){
  5548  		 /* Storage engine does not implement such method */
  5549  		 unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
  5550  		 rc = UNQLITE_NOTIMPLEMENTED;
  5551  	 }else{
  5552  		 if( nKeyLen < 0 ){
  5553  			 /* Assume a null terminated string and compute it's length */
  5554  			 nKeyLen = SyStrlen((const char *)pKey);
  5555  		 }
  5556  		 if( !nKeyLen ){
  5557  			 unqliteGenError(pDb,"Empty key");
  5558  			 rc = UNQLITE_EMPTY;
  5559  		 }else{
  5560  			 /* Perform the requested operation */
  5561  			 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,pData,nDataLen);
  5562  		 }
  5563  	 }
  5564  #if defined(UNQLITE_ENABLE_THREADS)
  5565  	 /* Leave DB mutex */
  5566  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5567  #endif
  5568  	return rc;
  5569  }
  5570  /*
  5571   * [CAPIREF: unqlite_kv_append_fmt()]
  5572   * Please refer to the official documentation for function purpose and expected parameters.
  5573   */
  5574  int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
  5575  {
  5576  	unqlite_kv_engine *pEngine;
  5577  	int rc;
  5578  	if( UNQLITE_DB_MISUSE(pDb) ){
  5579  		return UNQLITE_CORRUPT;
  5580  	}
  5581  #if defined(UNQLITE_ENABLE_THREADS)
  5582  	 /* Acquire DB mutex */
  5583  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5584  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5585  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5586  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5587  	 }
  5588  #endif
  5589  	 /* Point to the underlying storage engine */
  5590  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5591  	 if( pEngine->pIo->pMethods->xAppend == 0 ){
  5592  		 /* Storage engine does not implement such method */
  5593  		 unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
  5594  		 rc = UNQLITE_NOTIMPLEMENTED;
  5595  	 }else{
  5596  		 if( nKeyLen < 0 ){
  5597  			 /* Assume a null terminated string and compute it's length */
  5598  			 nKeyLen = SyStrlen((const char *)pKey);
  5599  		 }
  5600  		 if( !nKeyLen ){
  5601  			 unqliteGenError(pDb,"Empty key");
  5602  			 rc = UNQLITE_EMPTY;
  5603  		 }else{
  5604  			 SyBlob sWorker; /* Working buffer */
  5605  			 va_list ap;
  5606  			 SyBlobInit(&sWorker,&pDb->sMem);
  5607  			 /* Format the data */
  5608  			 va_start(ap,zFormat);
  5609  			 SyBlobFormatAp(&sWorker,zFormat,ap);
  5610  			 va_end(ap);
  5611  			 /* Perform the requested operation */
  5612  			 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
  5613  			 /* Clean up */
  5614  			 SyBlobRelease(&sWorker);
  5615  		 }
  5616  	 }
  5617  #if defined(UNQLITE_ENABLE_THREADS)
  5618  	 /* Leave DB mutex */
  5619  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5620  #endif
  5621  	return rc;
  5622  }
  5623  /*
  5624   * [CAPIREF: unqlite_kv_fetch()]
  5625   * Please refer to the official documentation for function purpose and expected parameters.
  5626   */
  5627  int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 *pBufLen)
  5628  {
  5629  	unqlite_kv_methods *pMethods;
  5630  	unqlite_kv_engine *pEngine;
  5631  	unqlite_kv_cursor *pCur;
  5632  	int rc;
  5633  	if( UNQLITE_DB_MISUSE(pDb) ){
  5634  		return UNQLITE_CORRUPT;
  5635  	}
  5636  #if defined(UNQLITE_ENABLE_THREADS)
  5637  	 /* Acquire DB mutex */
  5638  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5639  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5640  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5641  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5642  	 }
  5643  #endif
  5644  	 /* Point to the underlying storage engine */
  5645  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5646  	 pMethods = pEngine->pIo->pMethods;
  5647  	 pCur = pDb->sDB.pCursor;
  5648  	 if( nKeyLen < 0 ){
  5649  		 /* Assume a null terminated string and compute it's length */
  5650  		 nKeyLen = SyStrlen((const char *)pKey);
  5651  	 }
  5652  	 if( !nKeyLen ){
  5653  		  unqliteGenError(pDb,"Empty key");
  5654  		  rc = UNQLITE_EMPTY;
  5655  	 }else{
  5656  		  /* Seek to the record position */
  5657  		  rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
  5658  	 }
  5659  	 if( rc == UNQLITE_OK ){
  5660  		 if( pBuf == 0 ){
  5661  			 /* Data length only */
  5662  			 rc = pMethods->xDataLength(pCur,pBufLen);
  5663  		 }else{
  5664  			 SyBlob sBlob;
  5665  			 /* Initialize the data consumer */
  5666  			 SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)*pBufLen);
  5667  			 /* Consume the data */
  5668  			 rc = pMethods->xData(pCur,unqliteDataConsumer,&sBlob);
  5669  			 /* Data length */
  5670  			 *pBufLen = (unqlite_int64)SyBlobLength(&sBlob);
  5671  			 /* Cleanup */
  5672  			 SyBlobRelease(&sBlob);
  5673  		 }
  5674  	 }
  5675  #if defined(UNQLITE_ENABLE_THREADS)
  5676  	 /* Leave DB mutex */
  5677  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5678  #endif
  5679  	return rc;
  5680  }
  5681  /*
  5682   * [CAPIREF: unqlite_kv_fetch_callback()]
  5683   * Please refer to the official documentation for function purpose and expected parameters.
  5684   */
  5685  int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
  5686  {
  5687  	unqlite_kv_methods *pMethods;
  5688  	unqlite_kv_engine *pEngine;
  5689  	unqlite_kv_cursor *pCur;
  5690  	int rc;
  5691  	if( UNQLITE_DB_MISUSE(pDb) ){
  5692  		return UNQLITE_CORRUPT;
  5693  	}
  5694  #if defined(UNQLITE_ENABLE_THREADS)
  5695  	 /* Acquire DB mutex */
  5696  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5697  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5698  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5699  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5700  	 }
  5701  #endif
  5702  	 /* Point to the underlying storage engine */
  5703  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5704  	 pMethods = pEngine->pIo->pMethods;
  5705  	 pCur = pDb->sDB.pCursor;
  5706  	 if( nKeyLen < 0 ){
  5707  		 /* Assume a null terminated string and compute it's length */
  5708  		 nKeyLen = SyStrlen((const char *)pKey);
  5709  	 }
  5710  	 if( !nKeyLen ){
  5711  		 unqliteGenError(pDb,"Empty key");
  5712  		 rc = UNQLITE_EMPTY;
  5713  	 }else{
  5714  		 /* Seek to the record position */
  5715  		 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
  5716  	 }
  5717  	 if( rc == UNQLITE_OK && xConsumer ){
  5718  		 /* Consume the data directly */
  5719  		 rc = pMethods->xData(pCur,xConsumer,pUserData);	 
  5720  	 }
  5721  #if defined(UNQLITE_ENABLE_THREADS)
  5722  	 /* Leave DB mutex */
  5723  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5724  #endif
  5725  	return rc;
  5726  }
  5727  /*
  5728   * [CAPIREF: unqlite_kv_delete()]
  5729   * Please refer to the official documentation for function purpose and expected parameters.
  5730   */
  5731  int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen)
  5732  {
  5733  	unqlite_kv_methods *pMethods;
  5734  	unqlite_kv_engine *pEngine;
  5735  	unqlite_kv_cursor *pCur;
  5736  	int rc;
  5737  	if( UNQLITE_DB_MISUSE(pDb) ){
  5738  		return UNQLITE_CORRUPT;
  5739  	}
  5740  #if defined(UNQLITE_ENABLE_THREADS)
  5741  	 /* Acquire DB mutex */
  5742  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5743  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5744  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5745  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5746  	 }
  5747  #endif
  5748  	 /* Point to the underlying storage engine */
  5749  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5750  	 pMethods = pEngine->pIo->pMethods;
  5751  	 pCur = pDb->sDB.pCursor;
  5752  	 if( pMethods->xDelete == 0 ){
  5753  		 /* Storage engine does not implement such method */
  5754  		 unqliteGenError(pDb,"xDelete() method not implemented in the underlying storage engine");
  5755  		 rc = UNQLITE_NOTIMPLEMENTED;
  5756  	 }else{
  5757  		 if( nKeyLen < 0 ){
  5758  			 /* Assume a null terminated string and compute it's length */
  5759  			 nKeyLen = SyStrlen((const char *)pKey);
  5760  		 }
  5761  		 if( !nKeyLen ){
  5762  			 unqliteGenError(pDb,"Empty key");
  5763  			 rc = UNQLITE_EMPTY;
  5764  		 }else{
  5765  			 /* Seek to the record position */
  5766  			 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
  5767  		 }
  5768  		 if( rc == UNQLITE_OK ){
  5769  			 /* Exact match found, delete the entry */
  5770  			 rc = pMethods->xDelete(pCur);
  5771  		 }
  5772  	 }
  5773  #if defined(UNQLITE_ENABLE_THREADS)
  5774  	 /* Leave DB mutex */
  5775  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5776  #endif
  5777  	return rc;
  5778  }
  5779  /*
  5780   * [CAPIREF: unqlite_kv_config()]
  5781   * Please refer to the official documentation for function purpose and expected parameters.
  5782   */
  5783  int unqlite_kv_config(unqlite *pDb,int iOp,...)
  5784  {
  5785  	unqlite_kv_engine *pEngine;
  5786  	int rc;
  5787  	if( UNQLITE_DB_MISUSE(pDb) ){
  5788  		return UNQLITE_CORRUPT;
  5789  	}
  5790  #if defined(UNQLITE_ENABLE_THREADS)
  5791  	 /* Acquire DB mutex */
  5792  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5793  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5794  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5795  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5796  	 }
  5797  #endif
  5798  	 /* Point to the underlying storage engine */
  5799  	 pEngine = unqlitePagerGetKvEngine(pDb);
  5800  	 if( pEngine->pIo->pMethods->xConfig == 0 ){
  5801  		 /* Storage engine does not implements such method */
  5802  		 unqliteGenError(pDb,"xConfig() method not implemented in the underlying storage engine");
  5803  		 rc = UNQLITE_NOTIMPLEMENTED;
  5804  	 }else{
  5805  		 va_list ap;
  5806  		 /* Configure the storage engine */
  5807  		 va_start(ap,iOp);
  5808  		 rc = pEngine->pIo->pMethods->xConfig(pEngine,iOp,ap);
  5809  		 va_end(ap);
  5810  	 }
  5811  #if defined(UNQLITE_ENABLE_THREADS)
  5812  	 /* Leave DB mutex */
  5813  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5814  #endif
  5815  	return rc;
  5816  }
  5817  /*
  5818   * [CAPIREF: unqlite_kv_cursor_init()]
  5819   * Please refer to the official documentation for function purpose and expected parameters.
  5820   */
  5821  int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut)
  5822  {
  5823  	int rc;
  5824  	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0 /* Noop */){
  5825  		return UNQLITE_CORRUPT;
  5826  	}
  5827  #if defined(UNQLITE_ENABLE_THREADS)
  5828  	 /* Acquire DB mutex */
  5829  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5830  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5831  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5832  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5833  	 }
  5834  #endif
  5835  	 /* Allocate a new cursor */
  5836  	 rc = unqliteInitCursor(pDb,ppOut);
  5837  #if defined(UNQLITE_ENABLE_THREADS)
  5838  	 /* Leave DB mutex */
  5839  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5840  #endif
  5841  	 return rc;
  5842  }
  5843  /*
  5844   * [CAPIREF: unqlite_kv_cursor_release()]
  5845   * Please refer to the official documentation for function purpose and expected parameters.
  5846   */
  5847  int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur)
  5848  {
  5849  	int rc;
  5850  	if( UNQLITE_DB_MISUSE(pDb) || pCur == 0 /* Noop */){
  5851  		return UNQLITE_CORRUPT;
  5852  	}
  5853  #if defined(UNQLITE_ENABLE_THREADS)
  5854  	 /* Acquire DB mutex */
  5855  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5856  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  5857  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  5858  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  5859  	 }
  5860  #endif
  5861  	 /* Release the cursor */
  5862  	 rc = unqliteReleaseCursor(pDb,pCur);
  5863  #if defined(UNQLITE_ENABLE_THREADS)
  5864  	 /* Leave DB mutex */
  5865  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  5866  #endif
  5867  	 return rc;
  5868  }
  5869  /*
  5870   * [CAPIREF: unqlite_kv_cursor_first_entry()]
  5871   * Please refer to the official documentation for function purpose and expected parameters.
  5872   */
  5873  int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor)
  5874  {
  5875  	int rc;
  5876  #ifdef UNTRUST
  5877  	if( pCursor == 0 ){
  5878  		return UNQLITE_CORRUPT;
  5879  	}
  5880  #endif
  5881  	/* Check if the requested method is implemented by the underlying storage engine */
  5882  	if( pCursor->pStore->pIo->pMethods->xFirst == 0 ){
  5883  		rc = UNQLITE_NOTIMPLEMENTED;
  5884  	}else{
  5885  		/* Seek to the first entry */
  5886  		rc = pCursor->pStore->pIo->pMethods->xFirst(pCursor);
  5887  	}
  5888  	return rc;
  5889  }
  5890  /*
  5891   * [CAPIREF: unqlite_kv_cursor_last_entry()]
  5892   * Please refer to the official documentation for function purpose and expected parameters.
  5893   */
  5894  int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor)
  5895  {
  5896  	int rc;
  5897  #ifdef UNTRUST
  5898  	if( pCursor == 0 ){
  5899  		return UNQLITE_CORRUPT;
  5900  	}
  5901  #endif
  5902  	/* Check if the requested method is implemented by the underlying storage engine */
  5903  	if( pCursor->pStore->pIo->pMethods->xLast == 0 ){
  5904  		rc = UNQLITE_NOTIMPLEMENTED;
  5905  	}else{
  5906  		/* Seek to the last entry */
  5907  		rc = pCursor->pStore->pIo->pMethods->xLast(pCursor);
  5908  	}
  5909  	return rc;
  5910  }
  5911  /*
  5912   * [CAPIREF: unqlite_kv_cursor_valid_entry()]
  5913   * Please refer to the official documentation for function purpose and expected parameters.
  5914   */
  5915  int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor)
  5916  {
  5917  	int rc;
  5918  #ifdef UNTRUST
  5919  	if( pCursor == 0 ){
  5920  		return UNQLITE_CORRUPT;
  5921  	}
  5922  #endif
  5923  	/* Check if the requested method is implemented by the underlying storage engine */
  5924  	if( pCursor->pStore->pIo->pMethods->xValid == 0 ){
  5925  		rc = UNQLITE_NOTIMPLEMENTED;
  5926  	}else{
  5927  		rc = pCursor->pStore->pIo->pMethods->xValid(pCursor);
  5928  	}
  5929  	return rc;
  5930  }
  5931  /*
  5932   * [CAPIREF: unqlite_kv_cursor_next_entry()]
  5933   * Please refer to the official documentation for function purpose and expected parameters.
  5934   */
  5935  int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor)
  5936  {
  5937  	int rc;
  5938  #ifdef UNTRUST
  5939  	if( pCursor == 0 ){
  5940  		return UNQLITE_CORRUPT;
  5941  	}
  5942  #endif
  5943  	/* Check if the requested method is implemented by the underlying storage engine */
  5944  	if( pCursor->pStore->pIo->pMethods->xNext == 0 ){
  5945  		rc = UNQLITE_NOTIMPLEMENTED;
  5946  	}else{
  5947  		/* Seek to the next entry */
  5948  		rc = pCursor->pStore->pIo->pMethods->xNext(pCursor);
  5949  	}
  5950  	return rc;
  5951  }
  5952  /*
  5953   * [CAPIREF: unqlite_kv_cursor_prev_entry()]
  5954   * Please refer to the official documentation for function purpose and expected parameters.
  5955   */
  5956  int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor)
  5957  {
  5958  	int rc;
  5959  #ifdef UNTRUST
  5960  	if( pCursor == 0 ){
  5961  		return UNQLITE_CORRUPT;
  5962  	}
  5963  #endif
  5964  	/* Check if the requested method is implemented by the underlying storage engine */
  5965  	if( pCursor->pStore->pIo->pMethods->xPrev == 0 ){
  5966  		rc = UNQLITE_NOTIMPLEMENTED;
  5967  	}else{
  5968  		/* Seek to the previous entry */
  5969  		rc = pCursor->pStore->pIo->pMethods->xPrev(pCursor);
  5970  	}
  5971  	return rc;
  5972  }
  5973  /*
  5974   * [CAPIREF: unqlite_kv_cursor_delete_entry()]
  5975   * Please refer to the official documentation for function purpose and expected parameters.
  5976   */
  5977  int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor)
  5978  {
  5979  	int rc;
  5980  #ifdef UNTRUST
  5981  	if( pCursor == 0 ){
  5982  		return UNQLITE_CORRUPT;
  5983  	}
  5984  #endif
  5985  	/* Check if the requested method is implemented by the underlying storage engine */
  5986  	if( pCursor->pStore->pIo->pMethods->xDelete == 0 ){
  5987  		rc = UNQLITE_NOTIMPLEMENTED;
  5988  	}else{
  5989  		/* Delete the entry */
  5990  		rc = pCursor->pStore->pIo->pMethods->xDelete(pCursor);
  5991  	}
  5992  	return rc;
  5993  }
  5994  /*
  5995   * [CAPIREF: unqlite_kv_cursor_reset()]
  5996   * Please refer to the official documentation for function purpose and expected parameters.
  5997   */
  5998  int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor)
  5999  {
  6000  	int rc = UNQLITE_OK;
  6001  #ifdef UNTRUST
  6002  	if( pCursor == 0 ){
  6003  		return UNQLITE_CORRUPT;
  6004  	}
  6005  #endif
  6006  	/* Check if the requested method is implemented by the underlying storage engine */
  6007  	if( pCursor->pStore->pIo->pMethods->xReset == 0 ){
  6008  		rc = UNQLITE_NOTIMPLEMENTED;
  6009  	}else{
  6010  		/* Reset */
  6011  		pCursor->pStore->pIo->pMethods->xReset(pCursor);
  6012  	}
  6013  	return rc;
  6014  }
  6015  /*
  6016   * [CAPIREF: unqlite_kv_cursor_seek()]
  6017   * Please refer to the official documentation for function purpose and expected parameters.
  6018   */
  6019  int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos)
  6020  {
  6021  	int rc = UNQLITE_OK;
  6022  #ifdef UNTRUST
  6023  	if( pCursor == 0 ){
  6024  		return UNQLITE_CORRUPT;
  6025  	}
  6026  #endif
  6027  	if( nKeyLen < 0 ){
  6028  		/* Assume a null terminated string and compute it's length */
  6029  		nKeyLen = SyStrlen((const char *)pKey);
  6030  	}
  6031  	if( !nKeyLen ){
  6032  		rc = UNQLITE_EMPTY;
  6033  	}else{
  6034  		/* Seek to the desired location */
  6035  		rc = pCursor->pStore->pIo->pMethods->xSeek(pCursor,pKey,nKeyLen,iPos);
  6036  	}
  6037  	return rc;
  6038  }
  6039  /*
  6040   * Default data consumer callback. That is, all retrieved is redirected to this
  6041   * routine which store the output in an internal blob.
  6042   */
  6043  UNQLITE_PRIVATE int unqliteDataConsumer(
  6044  	const void *pOut,   /* Data to consume */
  6045  	unsigned int nLen,  /* Data length */
  6046  	void *pUserData     /* User private data */
  6047  	)
  6048  {
  6049  	 sxi32 rc;
  6050  	 /* Store the output in an internal BLOB */
  6051  	 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
  6052  	 return rc;
  6053  }
  6054  /*
  6055   * [CAPIREF: unqlite_kv_cursor_data_callback()]
  6056   * Please refer to the official documentation for function purpose and expected parameters.
  6057   */
  6058  int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
  6059  {
  6060  	int rc;
  6061  #ifdef UNTRUST
  6062  	if( pCursor == 0 ){
  6063  		return UNQLITE_CORRUPT;
  6064  	}
  6065  #endif
  6066  	/* Consume the key directly */
  6067  	rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,xConsumer,pUserData);
  6068  	return rc;
  6069  }
  6070  /*
  6071   * [CAPIREF: unqlite_kv_cursor_key()]
  6072   * Please refer to the official documentation for function purpose and expected parameters.
  6073   */
  6074  int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte)
  6075  {
  6076  	int rc;
  6077  #ifdef UNTRUST
  6078  	if( pCursor == 0 ){
  6079  		return UNQLITE_CORRUPT;
  6080  	}
  6081  #endif
  6082  	if( pBuf == 0 ){
  6083  		/* Key length only */
  6084  		rc = pCursor->pStore->pIo->pMethods->xKeyLength(pCursor,pnByte);
  6085  	}else{
  6086  		SyBlob sBlob;
  6087  		if( (*pnByte) < 0 ){
  6088  			return UNQLITE_CORRUPT;
  6089  		}
  6090  		/* Initialize the data consumer */
  6091  		SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
  6092  		/* Consume the key */
  6093  		rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,unqliteDataConsumer,&sBlob);
  6094  		 /* Key length */
  6095  		*pnByte = SyBlobLength(&sBlob);
  6096  		/* Cleanup */
  6097  		SyBlobRelease(&sBlob);
  6098  	}
  6099  	return rc;
  6100  }
  6101  /*
  6102   * [CAPIREF: unqlite_kv_cursor_data_callback()]
  6103   * Please refer to the official documentation for function purpose and expected parameters.
  6104   */
  6105  int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
  6106  {
  6107  	int rc;
  6108  #ifdef UNTRUST
  6109  	if( pCursor == 0 ){
  6110  		return UNQLITE_CORRUPT;
  6111  	}
  6112  #endif
  6113  	/* Consume the data directly */
  6114  	rc = pCursor->pStore->pIo->pMethods->xData(pCursor,xConsumer,pUserData);
  6115  	return rc;
  6116  }
  6117  /*
  6118   * [CAPIREF: unqlite_kv_cursor_data()]
  6119   * Please refer to the official documentation for function purpose and expected parameters.
  6120   */
  6121  int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnByte)
  6122  {
  6123  	int rc;
  6124  #ifdef UNTRUST
  6125  	if( pCursor == 0 ){
  6126  		return UNQLITE_CORRUPT;
  6127  	}
  6128  #endif
  6129  	if( pBuf == 0 ){
  6130  		/* Data length only */
  6131  		rc = pCursor->pStore->pIo->pMethods->xDataLength(pCursor,pnByte);
  6132  	}else{
  6133  		SyBlob sBlob;
  6134  		if( (*pnByte) < 0 ){
  6135  			return UNQLITE_CORRUPT;
  6136  		}
  6137  		/* Initialize the data consumer */
  6138  		SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
  6139  		/* Consume the data */
  6140  		rc = pCursor->pStore->pIo->pMethods->xData(pCursor,unqliteDataConsumer,&sBlob);
  6141  		/* Data length */
  6142  		*pnByte = SyBlobLength(&sBlob);
  6143  		/* Cleanup */
  6144  		SyBlobRelease(&sBlob);
  6145  	}
  6146  	return rc;
  6147  }
  6148  /*
  6149   * [CAPIREF: unqlite_begin()]
  6150   * Please refer to the official documentation for function purpose and expected parameters.
  6151   */
  6152  int unqlite_begin(unqlite *pDb)
  6153  {
  6154  	int rc;
  6155  	if( UNQLITE_DB_MISUSE(pDb) ){
  6156  		return UNQLITE_CORRUPT;
  6157  	}
  6158  #if defined(UNQLITE_ENABLE_THREADS)
  6159  	 /* Acquire DB mutex */
  6160  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6161  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  6162  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  6163  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  6164  	 }
  6165  #endif
  6166  	 /* Begin the write transaction */
  6167  	 rc = unqlitePagerBegin(pDb->sDB.pPager);
  6168  #if defined(UNQLITE_ENABLE_THREADS)
  6169  	 /* Leave DB mutex */
  6170  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6171  #endif
  6172  	 return rc;
  6173  }
  6174  /*
  6175   * [CAPIREF: unqlite_commit()]
  6176   * Please refer to the official documentation for function purpose and expected parameters.
  6177   */
  6178  int unqlite_commit(unqlite *pDb)
  6179  {
  6180  	int rc;
  6181  	if( UNQLITE_DB_MISUSE(pDb) ){
  6182  		return UNQLITE_CORRUPT;
  6183  	}
  6184  #if defined(UNQLITE_ENABLE_THREADS)
  6185  	 /* Acquire DB mutex */
  6186  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6187  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  6188  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  6189  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  6190  	 }
  6191  #endif
  6192  	 /* Commit the transaction */
  6193  	 rc = unqlitePagerCommit(pDb->sDB.pPager);
  6194  #if defined(UNQLITE_ENABLE_THREADS)
  6195  	 /* Leave DB mutex */
  6196  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6197  #endif
  6198  	 return rc;
  6199  }
  6200  /*
  6201   * [CAPIREF: unqlite_rollback()]
  6202   * Please refer to the official documentation for function purpose and expected parameters.
  6203   */
  6204  int unqlite_rollback(unqlite *pDb)
  6205  {
  6206  	int rc;
  6207  	if( UNQLITE_DB_MISUSE(pDb) ){
  6208  		return UNQLITE_CORRUPT;
  6209  	}
  6210  #if defined(UNQLITE_ENABLE_THREADS)
  6211  	 /* Acquire DB mutex */
  6212  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6213  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  6214  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  6215  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  6216  	 }
  6217  #endif
  6218  	 /* Rollback the transaction */
  6219  	 rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
  6220  #if defined(UNQLITE_ENABLE_THREADS)
  6221  	 /* Leave DB mutex */
  6222  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6223  #endif
  6224  	 return rc;
  6225  }
  6226  /*
  6227   * [CAPIREF: unqlite_util_load_mmaped_file()]
  6228   * Please refer to the official documentation for function purpose and expected parameters.
  6229   */
  6230  UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize)
  6231  {
  6232  	const jx9_vfs *pVfs;
  6233  	int rc;
  6234  	if( SX_EMPTY_STR(zFile) || ppMap == 0 || pFileSize == 0){
  6235  		/* Sanity check */
  6236  		return UNQLITE_CORRUPT;
  6237  	}
  6238  	*ppMap = 0;
  6239  	/* Extract the Jx9 Vfs */
  6240  	pVfs = jx9ExportBuiltinVfs();
  6241  	/*
  6242  	 * Check if the underlying vfs implement the memory map routines
  6243  	 * [i.e: mmap() under UNIX/MapViewOfFile() under windows].
  6244  	 */
  6245  	if( pVfs == 0 || pVfs->xMmap == 0 ){
  6246  		rc = UNQLITE_NOTIMPLEMENTED;
  6247  	 }else{ 
  6248  		 /* Try to get a read-only memory view of the whole file */
  6249  		 rc = pVfs->xMmap(zFile,ppMap,pFileSize);
  6250  	 }
  6251  	return rc;
  6252  }
  6253  /*
  6254   * [CAPIREF: unqlite_util_release_mmaped_file()]
  6255   * Please refer to the official documentation for function purpose and expected parameters.
  6256   */
  6257  UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize)
  6258  {
  6259  	const jx9_vfs *pVfs;
  6260  	int rc = UNQLITE_OK;
  6261  	if( pMap == 0 ){
  6262  		return UNQLITE_OK;
  6263  	}
  6264  	/* Extract the Jx9 Vfs */
  6265  	pVfs = jx9ExportBuiltinVfs();
  6266  	if( pVfs == 0 || pVfs->xUnmap == 0 ){
  6267  		rc = UNQLITE_NOTIMPLEMENTED;
  6268  	 }else{ 
  6269  		 pVfs->xUnmap(pMap,iFileSize);
  6270  	 }
  6271  	return rc;
  6272  }
  6273  /*
  6274   * [CAPIREF: unqlite_util_random_string()]
  6275   * Please refer to the official documentation for function purpose and expected parameters.
  6276   */
  6277  UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size)
  6278  {
  6279  	if( UNQLITE_DB_MISUSE(pDb) ){
  6280  		return UNQLITE_CORRUPT;
  6281  	}
  6282  	if( zBuf == 0 || buf_size < 3 ){
  6283  		/* Buffer must be long enough to hold three bytes */
  6284  		return UNQLITE_INVALID;
  6285  	}
  6286  #if defined(UNQLITE_ENABLE_THREADS)
  6287  	 /* Acquire DB mutex */
  6288  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6289  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  6290  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  6291  			 return UNQLITE_ABORT; /* Another thread have released this instance */
  6292  	 }
  6293  #endif
  6294  	 /* Generate the random string */
  6295  	 unqlitePagerRandomString(pDb->sDB.pPager,zBuf,buf_size);
  6296  #if defined(UNQLITE_ENABLE_THREADS)
  6297  	 /* Leave DB mutex */
  6298  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6299  #endif
  6300  	 return UNQLITE_OK;
  6301  }
  6302  /*
  6303   * [CAPIREF: unqlite_util_random_num()]
  6304   * Please refer to the official documentation for function purpose and expected parameters.
  6305   */
  6306  UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb)
  6307  {
  6308  	sxu32 iNum;
  6309  	if( UNQLITE_DB_MISUSE(pDb) ){
  6310  		return 0;
  6311  	}
  6312  #if defined(UNQLITE_ENABLE_THREADS)
  6313  	 /* Acquire DB mutex */
  6314  	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6315  	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
  6316  		 UNQLITE_THRD_DB_RELEASE(pDb) ){
  6317  			 return 0; /* Another thread have released this instance */
  6318  	 }
  6319  #endif
  6320  	 /* Generate the random number */
  6321  	 iNum = unqlitePagerRandomNum(pDb->sDB.pPager);
  6322  #if defined(UNQLITE_ENABLE_THREADS)
  6323  	 /* Leave DB mutex */
  6324  	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
  6325  #endif
  6326  	 return iNum;
  6327  }
  6328  /*
  6329   * ----------------------------------------------------------
  6330   * File: bitvec.c
  6331   * MD5: 7e3376710d8454ebcf8c77baacca880f
  6332   * ----------------------------------------------------------
  6333   */
  6334  /*
  6335   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
  6336   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
  6337   * Version 1.1.6
  6338   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  6339   * please contact Symisc Systems via:
  6340   *       legal@symisc.net
  6341   *       licensing@symisc.net
  6342   *       contact@symisc.net
  6343   * or visit:
  6344   *      http://unqlite.org/licensing.html
  6345   */
  6346   /* $SymiscID: bitvec.c v1.0 Win7 2013-02-27 15:16 stable <chm@symisc.net> $ */
  6347  #ifndef UNQLITE_AMALGAMATION
  6348  #include "unqliteInt.h"
  6349  #endif
  6350  
  6351  /** This file implements an object that represents a dynmaic
  6352  ** bitmap.
  6353  **
  6354  ** A bitmap is used to record which pages of a database file have been
  6355  ** journalled during a transaction, or which pages have the "dont-write"
  6356  ** property.  Usually only a few pages are meet either condition.
  6357  ** So the bitmap is usually sparse and has low cardinality.
  6358  */
  6359  /*
  6360   * Actually, this is not a bitmap but a simple hashtable where page 
  6361   * number (64-bit unsigned integers) are used as the lookup keys.
  6362   */
  6363  typedef struct bitvec_rec bitvec_rec;
  6364  struct bitvec_rec
  6365  {
  6366  	pgno iPage;                  /* Page number */
  6367  	bitvec_rec *pNext,*pNextCol; /* Collison link */
  6368  };
  6369  struct Bitvec
  6370  {
  6371  	SyMemBackend *pAlloc; /* Memory allocator */
  6372  	sxu32 nRec;           /* Total number of records */
  6373  	sxu32 nSize;          /* Table size */
  6374  	bitvec_rec **apRec;   /* Record table */
  6375  	bitvec_rec *pList;    /* List of records */
  6376  };
  6377  /* 
  6378   * Allocate a new bitvec instance.
  6379  */
  6380  UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize)
  6381  {
  6382  	bitvec_rec **apNew;
  6383  	Bitvec *p;
  6384  	
  6385  	p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) );
  6386  	if( p == 0 ){
  6387  		SXUNUSED(iSize); /* cc warning */
  6388  		return 0;
  6389  	}
  6390  	/* Zero the structure */
  6391  	SyZero(p,sizeof(Bitvec));
  6392  	/* Allocate a new table */
  6393  	p->nSize = 64; /* Must be a power of two */
  6394  	apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *));
  6395  	if( apNew == 0 ){
  6396  		SyMemBackendFree(pAlloc,p);
  6397  		return 0;
  6398  	}
  6399  	/* Zero the new table */
  6400  	SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *));
  6401  	/* Fill-in */
  6402  	p->apRec = apNew;
  6403  	p->pAlloc = pAlloc;
  6404  	return p;
  6405  }
  6406  /*
  6407   * Check if the given page number is already installed in the table.
  6408   * Return true if installed. False otherwise.
  6409   */
  6410  UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i)
  6411  {  
  6412  	bitvec_rec *pRec;
  6413  	/* Point to the desired bucket */
  6414  	pRec = p->apRec[i & (p->nSize - 1)];
  6415  	for(;;){
  6416  		if( pRec == 0 ){ break; }
  6417  		if( pRec->iPage == i ){
  6418  			/* Page found */
  6419  			return 1;
  6420  		}
  6421  		/* Point to the next entry */
  6422  		pRec = pRec->pNextCol;
  6423  
  6424  		if( pRec == 0 ){ break; }
  6425  		if( pRec->iPage == i ){
  6426  			/* Page found */
  6427  			return 1;
  6428  		}
  6429  		/* Point to the next entry */
  6430  		pRec = pRec->pNextCol;
  6431  
  6432  
  6433  		if( pRec == 0 ){ break; }
  6434  		if( pRec->iPage == i ){
  6435  			/* Page found */
  6436  			return 1;
  6437  		}
  6438  		/* Point to the next entry */
  6439  		pRec = pRec->pNextCol;
  6440  
  6441  
  6442  		if( pRec == 0 ){ break; }
  6443  		if( pRec->iPage == i ){
  6444  			/* Page found */
  6445  			return 1;
  6446  		}
  6447  		/* Point to the next entry */
  6448  		pRec = pRec->pNextCol;
  6449  	}
  6450  	/* No such entry */
  6451  	return 0;
  6452  }
  6453  /*
  6454   * Install a given page number in our bitmap (Actually, our hashtable).
  6455   */
  6456  UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i)
  6457  {
  6458  	bitvec_rec *pRec;
  6459  	sxi32 iBuck;
  6460  	/* Allocate a new instance */
  6461  	pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec));
  6462  	if( pRec == 0 ){
  6463  		return UNQLITE_NOMEM;
  6464  	}
  6465  	/* Zero the structure */
  6466  	SyZero(pRec,sizeof(bitvec_rec));
  6467  	/* Fill-in */
  6468  	pRec->iPage = i;
  6469  	iBuck = i & (p->nSize - 1);
  6470  	pRec->pNextCol = p->apRec[iBuck];
  6471  	p->apRec[iBuck] = pRec;
  6472  	pRec->pNext = p->pList;
  6473  	p->pList = pRec;
  6474  	p->nRec++;
  6475  	if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){
  6476  		/* Grow the hashtable */
  6477  		sxu32 nNewSize = p->nSize << 1;
  6478  		bitvec_rec *pEntry,**apNew;
  6479  		sxu32 n;
  6480  		apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *));
  6481  		if( apNew ){
  6482  			sxu32 iBucket;
  6483  			/* Zero the new table */
  6484  			SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *));
  6485  			/* Rehash all entries */
  6486  			n = 0;
  6487  			pEntry = p->pList;
  6488  			for(;;){
  6489  				/* Loop one */
  6490  				if( n >= p->nRec ){
  6491  					break;
  6492  				}
  6493  				pEntry->pNextCol = 0;
  6494  				/* Install in the new bucket */
  6495  				iBucket = pEntry->iPage & (nNewSize - 1);
  6496  				pEntry->pNextCol = apNew[iBucket];
  6497  				apNew[iBucket] = pEntry;
  6498  				/* Point to the next entry */
  6499  				pEntry = pEntry->pNext;
  6500  				n++;
  6501  			}
  6502  			/* Release the old table and reflect the change */
  6503  			SyMemBackendFree(p->pAlloc,(void *)p->apRec);
  6504  			p->apRec = apNew;
  6505  			p->nSize  = nNewSize;
  6506  		}
  6507  	}
  6508  	return UNQLITE_OK;
  6509  }
  6510  /*
  6511   * Destroy a bitvec instance. Reclaim all memory used.
  6512   */
  6513  UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p)
  6514  {
  6515  	bitvec_rec *pNext,*pRec = p->pList;
  6516  	SyMemBackend *pAlloc = p->pAlloc;
  6517  	
  6518  	for(;;){
  6519  		if( p->nRec < 1 ){
  6520  			break;
  6521  		}
  6522  		pNext = pRec->pNext;
  6523  		SyMemBackendPoolFree(pAlloc,(void *)pRec);
  6524  		pRec = pNext;
  6525  		p->nRec--;
  6526  
  6527  		if( p->nRec < 1 ){
  6528  			break;
  6529  		}
  6530  		pNext = pRec->pNext;
  6531  		SyMemBackendPoolFree(pAlloc,(void *)pRec);
  6532  		pRec = pNext;
  6533  		p->nRec--;
  6534  
  6535  
  6536  		if( p->nRec < 1 ){
  6537  			break;
  6538  		}
  6539  		pNext = pRec->pNext;
  6540  		SyMemBackendPoolFree(pAlloc,(void *)pRec);
  6541  		pRec = pNext;
  6542  		p->nRec--;
  6543  
  6544  
  6545  		if( p->nRec < 1 ){
  6546  			break;
  6547  		}
  6548  		pNext = pRec->pNext;
  6549  		SyMemBackendPoolFree(pAlloc,(void *)pRec);
  6550  		pRec = pNext;
  6551  		p->nRec--;
  6552  	}
  6553  	SyMemBackendFree(pAlloc,(void *)p->apRec);
  6554  	SyMemBackendFree(pAlloc,p);
  6555  }
  6556  /*
  6557   * ----------------------------------------------------------
  6558   * File: fastjson.c
  6559   * MD5: 3693c0022edc7d37b65124d7aef68397
  6560   * ----------------------------------------------------------
  6561   */
  6562  /*
  6563   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
  6564   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
  6565   * Version 1.1.6
  6566   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  6567   * please contact Symisc Systems via:
  6568   *       legal@symisc.net
  6569   *       licensing@symisc.net
  6570   *       contact@symisc.net
  6571   * or visit:
  6572   *      http://unqlite.org/licensing.html
  6573   */
  6574   /* $SymiscID: fastjson.c v1.1 FreeBSD 2012-12-05 22:52 stable <chm@symisc.net> $ */
  6575  #ifndef UNQLITE_AMALGAMATION
  6576  #include "unqliteInt.h"
  6577  #endif
  6578  /* JSON binary encoding, decoding and stuff like that */
  6579  #ifndef UNQLITE_FAST_JSON_NEST_LIMIT
  6580  #if defined(__WINNT__) || defined(__UNIXES__)
  6581  #define UNQLITE_FAST_JSON_NEST_LIMIT 64 /* Nesting limit */
  6582  #else
  6583  #define UNQLITE_FAST_JSON_NEST_LIMIT 32 /* Nesting limit */
  6584  #endif
  6585  #endif /* UNQLITE_FAST_JSON_NEST_LIMIT */
  6586  /* 
  6587   * JSON to Binary using the FastJSON implementation (BigEndian).
  6588   */
  6589  /*
  6590   * FastJSON implemented binary token.
  6591   */
  6592  #define FJSON_DOC_START    1 /* { */
  6593  #define FJSON_DOC_END      2 /* } */
  6594  #define FJSON_ARRAY_START  3 /* [ */
  6595  #define FJSON_ARRAY_END    4 /* ] */
  6596  #define FJSON_COLON        5 /* : */
  6597  #define FJSON_COMMA        6 /* , */
  6598  #define FJSON_ID           7 /* ID + 4 Bytes length */
  6599  #define FJSON_STRING       8 /* String + 4 bytes length */
  6600  #define FJSON_BYTE         9 /* Byte */
  6601  #define FJSON_INT64       10 /* Integer 64 + 8 bytes */
  6602  #define FJSON_REAL        18 /* Floating point value + 2 bytes */
  6603  #define FJSON_NULL        23 /* NULL */
  6604  #define FJSON_TRUE        24 /* TRUE */
  6605  #define FJSON_FALSE       25 /* FALSE */
  6606  /*
  6607   * Encode a Jx9 value to binary JSON.
  6608   */
  6609  UNQLITE_PRIVATE sxi32 FastJsonEncode(
  6610  	jx9_value *pValue, /* Value to encode */
  6611  	SyBlob *pOut,      /* Store encoded value here */
  6612  	int iNest          /* Nesting limit */ 
  6613  	)
  6614  {
  6615  	sxi32 iType = pValue ? pValue->iFlags : MEMOBJ_NULL;
  6616  	sxi32 rc = SXRET_OK;
  6617  	int c;
  6618  	if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
  6619  		/* Nesting limit reached */
  6620  		return SXERR_LIMIT;
  6621  	}
  6622  	if( iType & (MEMOBJ_NULL|MEMOBJ_RES) ){
  6623  		/*
  6624  		 * Resources are encoded as null also.
  6625  		 */
  6626  		c = FJSON_NULL;
  6627  		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6628  	}else if( iType & MEMOBJ_BOOL ){
  6629  		c = pValue->x.iVal ? FJSON_TRUE : FJSON_FALSE;
  6630  		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6631  	}else if( iType & MEMOBJ_STRING ){
  6632  		unsigned char zBuf[sizeof(sxu32)]; /* String length */
  6633  		c = FJSON_STRING;
  6634  		SyBigEndianPack32(zBuf,SyBlobLength(&pValue->sBlob));
  6635  		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6636  		if( rc == SXRET_OK ){
  6637  			rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
  6638  			if( rc == SXRET_OK ){
  6639  				rc = SyBlobAppend(pOut,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob));
  6640  			}
  6641  		}
  6642  	}else if( iType & MEMOBJ_INT ){
  6643  		unsigned char zBuf[8];
  6644  		/* 64bit big endian integer */
  6645  		c = FJSON_INT64;
  6646  		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6647  		if( rc == SXRET_OK ){
  6648  			SyBigEndianPack64(zBuf,(sxu64)pValue->x.iVal);
  6649  			rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
  6650  		}
  6651  	}else if( iType & MEMOBJ_REAL ){
  6652  		/* Real number */
  6653  		c = FJSON_REAL;
  6654  		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6655  		if( rc == SXRET_OK ){
  6656  			sxu32 iOfft = SyBlobLength(pOut);
  6657  			rc = SyBlobAppendBig16(pOut,0);
  6658  			if( rc == SXRET_OK ){
  6659  				unsigned char *zBlob;
  6660  				SyBlobFormat(pOut,"%.15g",pValue->x.rVal);
  6661  				zBlob = (unsigned char *)SyBlobDataAt(pOut,iOfft);
  6662  				SyBigEndianPack16(zBlob,(sxu16)(SyBlobLength(pOut) - ( 2 + iOfft)));
  6663  			}
  6664  		}
  6665  	}else if( iType & MEMOBJ_HASHMAP ){
  6666  		/* A JSON object or array */
  6667  		jx9_hashmap *pMap = (jx9_hashmap *)pValue->x.pOther;
  6668  		jx9_hashmap_node *pNode;
  6669  		jx9_value *pEntry;
  6670  		/* Reset the hashmap loop cursor */
  6671  		jx9HashmapResetLoopCursor(pMap);
  6672  		if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
  6673  			jx9_value sKey;
  6674  			/* A JSON object */
  6675  			c = FJSON_DOC_START; /* { */
  6676  			rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6677  			if( rc == SXRET_OK ){
  6678  				jx9MemObjInit(pMap->pVm,&sKey);
  6679  				/* Encode object entries */
  6680  				while((pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
  6681  					/* Extract the key */
  6682  					jx9HashmapExtractNodeKey(pNode,&sKey);
  6683  					/* Encode it */
  6684  					rc = FastJsonEncode(&sKey,pOut,iNest+1);
  6685  					if( rc != SXRET_OK ){
  6686  						break;
  6687  					}
  6688  					c = FJSON_COLON;
  6689  					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6690  					if( rc != SXRET_OK ){
  6691  						break;
  6692  					}
  6693  					/* Extract the value */
  6694  					pEntry = jx9HashmapGetNodeValue(pNode);
  6695  					/* Encode it */
  6696  					rc = FastJsonEncode(pEntry,pOut,iNest+1);
  6697  					if( rc != SXRET_OK ){
  6698  						break;
  6699  					}
  6700  					/* Delimit the entry */
  6701  					c = FJSON_COMMA;
  6702  					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6703  					if( rc != SXRET_OK ){
  6704  						break;
  6705  					}
  6706  				}
  6707  				jx9MemObjRelease(&sKey);
  6708  				if( rc == SXRET_OK ){
  6709  					c = FJSON_DOC_END; /* } */
  6710  					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6711  				}
  6712  			}
  6713  		}else{
  6714  			/* A JSON array */
  6715  			c = FJSON_ARRAY_START; /* [ */
  6716  			rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6717  			if( rc == SXRET_OK ){
  6718  				/* Encode array entries */
  6719  				while( (pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
  6720  					/* Extract the value */
  6721  					pEntry = jx9HashmapGetNodeValue(pNode);
  6722  					/* Encode it */
  6723  					rc = FastJsonEncode(pEntry,pOut,iNest+1);
  6724  					if( rc != SXRET_OK ){
  6725  						break;
  6726  					}
  6727  					/* Delimit the entry */
  6728  					c = FJSON_COMMA;
  6729  					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6730  					if( rc != SXRET_OK ){
  6731  						break;
  6732  					}
  6733  				}
  6734  				if( rc == SXRET_OK ){
  6735  					c = FJSON_ARRAY_END; /* ] */
  6736  					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
  6737  				}
  6738  			}
  6739  		}
  6740  	}
  6741  	return rc;
  6742  }
  6743  /*
  6744   * Decode a FastJSON binary blob.
  6745   */
  6746  UNQLITE_PRIVATE sxi32 FastJsonDecode(
  6747  	const void *pIn,  /* Binary JSON  */
  6748  	sxu32 nByte,      /* Chunk delimiter */
  6749  	jx9_value *pOut,  /* Decoded value */
  6750  	const unsigned char **pzPtr,
  6751  	int iNest /* Nesting limit */
  6752  	)
  6753  {
  6754  	const unsigned char *zIn = (const unsigned char *)pIn;
  6755  	const unsigned char *zEnd = &zIn[nByte];
  6756  	sxi32 rc = SXRET_OK;
  6757  	int c;
  6758  	if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
  6759  		/* Nesting limit reached */
  6760  		return SXERR_LIMIT;
  6761  	}
  6762  	c = zIn[0];
  6763  	/* Advance the stream cursor */
  6764  	zIn++;
  6765  	/* Process the binary token */
  6766  	switch(c){
  6767  	case FJSON_NULL:
  6768  		/* null */
  6769  		jx9_value_null(pOut);
  6770  		break;
  6771  	case FJSON_FALSE:
  6772  		/* Boolean FALSE */
  6773  		jx9_value_bool(pOut,0);
  6774  		break;
  6775  	case FJSON_TRUE:
  6776  		/* Boolean TRUE */
  6777  		jx9_value_bool(pOut,1);
  6778  		break;
  6779  	case FJSON_INT64: {
  6780  		/* 64Bit integer */
  6781  		sxu64 iVal;
  6782  		/* Sanity check */
  6783  		if( &zIn[8] >= zEnd ){
  6784  			/* Corrupt chunk */
  6785  			rc = SXERR_CORRUPT;
  6786  			break;
  6787  		}
  6788  		SyBigEndianUnpack64(zIn,&iVal);
  6789  		/* Advance the pointer */
  6790  		zIn += 8;
  6791  		jx9_value_int64(pOut,(jx9_int64)iVal);
  6792  		break;
  6793  					  }
  6794  	case FJSON_REAL: {
  6795  		/* Real number */
  6796  		double iVal = 0; /* cc warning */
  6797  		sxu16 iLen;
  6798  		/* Sanity check */
  6799  		if( &zIn[2] >= zEnd ){
  6800  			/* Corrupt chunk */
  6801  			rc = SXERR_CORRUPT;
  6802  			break;
  6803  		}
  6804  		SyBigEndianUnpack16(zIn,&iLen);
  6805  		if( &zIn[iLen] >= zEnd ){
  6806  			/* Corrupt chunk */
  6807  			rc = SXERR_CORRUPT;
  6808  			break;
  6809  		}
  6810  		zIn += 2;
  6811  		SyStrToReal((const char *)zIn,(sxu32)iLen,&iVal,0);
  6812  		/* Advance the pointer */
  6813  		zIn += iLen;
  6814  		jx9_value_double(pOut,iVal);
  6815  		break;
  6816  					 }
  6817  	case FJSON_STRING: {
  6818  		/* UTF-8/Binary chunk */
  6819  		sxu32 iLength;
  6820  		/* Sanity check */
  6821  		if( &zIn[4] >= zEnd ){
  6822  			/* Corrupt chunk */
  6823  			rc = SXERR_CORRUPT;
  6824  			break;
  6825  		}
  6826  		SyBigEndianUnpack32(zIn,&iLength);
  6827  		if( &zIn[iLength] >= zEnd ){
  6828  			/* Corrupt chunk */
  6829  			rc = SXERR_CORRUPT;
  6830  			break;
  6831  		}
  6832  		zIn += 4;
  6833  		/* Invalidate any prior representation */
  6834  		if( pOut->iFlags & MEMOBJ_STRING ){
  6835  			/* Reset the string cursor */
  6836  			SyBlobReset(&pOut->sBlob);
  6837  		}
  6838  		rc = jx9MemObjStringAppend(pOut,(const char *)zIn,iLength);
  6839  		/* Update pointer */
  6840  		zIn += iLength;
  6841  		break;
  6842  					   }
  6843  	case FJSON_ARRAY_START: {
  6844  		/* Binary JSON array */
  6845  		jx9_hashmap *pMap;
  6846  		jx9_value sVal;
  6847  		/* Allocate a new hashmap */
  6848  		pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
  6849  		if( pMap == 0 ){
  6850  			rc = SXERR_MEM;
  6851  			break;
  6852  		}
  6853  		jx9MemObjInit(pOut->pVm,&sVal);
  6854  		jx9MemObjRelease(pOut);
  6855  		MemObjSetType(pOut,MEMOBJ_HASHMAP);
  6856  		pOut->x.pOther = pMap;
  6857  		rc = SXRET_OK;
  6858  		for(;;){
  6859  			/* Jump leading binary commas */
  6860  			while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
  6861  				zIn++;
  6862  			}
  6863  			if( zIn >= zEnd || zIn[0] == FJSON_ARRAY_END ){
  6864  				if( zIn < zEnd ){
  6865  					zIn++; /* Jump the trailing binary ] */
  6866  				}
  6867  				break;
  6868  			}
  6869  			/* Decode the value */
  6870  			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
  6871  			if( rc != SXRET_OK ){
  6872  				break;
  6873  			}
  6874  			/* Insert the decoded value */
  6875  			rc = jx9HashmapInsert(pMap,0,&sVal);
  6876  			if( rc != UNQLITE_OK ){
  6877  				break;
  6878  			}
  6879  		}
  6880  		if( rc != SXRET_OK ){
  6881  			jx9MemObjRelease(pOut);
  6882  		}
  6883  		jx9MemObjRelease(&sVal);
  6884  		break;
  6885  							}
  6886  	case FJSON_DOC_START: {
  6887  		/* Binary JSON object */
  6888  		jx9_value sVal,sKey;
  6889  		jx9_hashmap *pMap;
  6890  		/* Allocate a new hashmap */
  6891  		pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
  6892  		if( pMap == 0 ){
  6893  			rc = SXERR_MEM;
  6894  			break;
  6895  		}
  6896  		jx9MemObjInit(pOut->pVm,&sVal);
  6897  		jx9MemObjInit(pOut->pVm,&sKey);
  6898  		jx9MemObjRelease(pOut);
  6899  		MemObjSetType(pOut,MEMOBJ_HASHMAP);
  6900  		pOut->x.pOther = pMap;
  6901  		rc = SXRET_OK;
  6902  		for(;;){
  6903  			/* Jump leading binary commas */
  6904  			while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
  6905  				zIn++;
  6906  			}
  6907  			if( zIn >= zEnd || zIn[0] == FJSON_DOC_END ){
  6908  				if( zIn < zEnd ){
  6909  					zIn++; /* Jump the trailing binary } */
  6910  				}
  6911  				break;
  6912  			}
  6913  			/* Extract the key */
  6914  			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sKey,&zIn,iNest+1);
  6915  			if( rc != UNQLITE_OK ){
  6916  				break;
  6917  			}
  6918  			if( zIn >= zEnd || zIn[0] != FJSON_COLON ){
  6919  				rc = UNQLITE_CORRUPT;
  6920  				break;
  6921  			}
  6922  			zIn++; /* Jump the binary colon ':' */
  6923  			if( zIn >= zEnd ){
  6924  				rc = UNQLITE_CORRUPT;
  6925  				break;
  6926  			}
  6927  			/* Decode the value */
  6928  			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
  6929  			if( rc != SXRET_OK ){
  6930  				break;
  6931  			}
  6932  			/* Insert the key and its associated value */
  6933  			rc = jx9HashmapInsert(pMap,&sKey,&sVal);
  6934  			if( rc != UNQLITE_OK ){
  6935  				break;
  6936  			}
  6937  		}
  6938  		if( rc != SXRET_OK ){
  6939  			jx9MemObjRelease(pOut);
  6940  		}
  6941  		jx9MemObjRelease(&sVal);
  6942  		jx9MemObjRelease(&sKey);
  6943  		break;
  6944  						  }
  6945  	default:
  6946  		/* Corrupt data */
  6947  		rc = SXERR_CORRUPT;
  6948  		break;
  6949  	}
  6950  	if( pzPtr ){
  6951  		*pzPtr = zIn;
  6952  	}
  6953  	return rc;
  6954  }
  6955  /*
  6956   * ----------------------------------------------------------
  6957   * File: jx9_api.c
  6958   * MD5: 73cba599c009cee0ff878666d0543438
  6959   * ----------------------------------------------------------
  6960   */
  6961  /*
  6962   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  6963   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  6964   * Version 1.7.2
  6965   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  6966   * please contact Symisc Systems via:
  6967   *       legal@symisc.net
  6968   *       licensing@symisc.net
  6969   *       contact@symisc.net
  6970   * or visit:
  6971   *      http://jx9.symisc.net/
  6972   */
  6973   /* $SymiscID: api.c v1.7 FreeBSD 2012-12-18 06:54 stable <chm@symisc.net> $ */
  6974  #ifndef JX9_AMALGAMATION
  6975  #include "jx9Int.h"
  6976  #endif
  6977  /* This file implement the public interfaces presented to host-applications.
  6978   * Routines in other files are for internal use by JX9 and should not be
  6979   * accessed by users of the library.
  6980   */
  6981  #define JX9_ENGINE_MAGIC 0xF874BCD7
  6982  #define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC)
  6983  #define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
  6984  /* If another thread have released a working instance, the following macros
  6985   * evaluates to true. These macros are only used when the library
  6986   * is built with threading support enabled which is not the case in
  6987   * the default built.
  6988   */
  6989  #define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC)
  6990  #define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
  6991  /* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */
  6992  /*
  6993   * All global variables are collected in the structure named "sJx9MPGlobal".
  6994   * That way it is clear in the code when we are using static variable because
  6995   * its name start with sJx9MPGlobal.
  6996   */
  6997  static struct Jx9Global_Data
  6998  {
  6999  	SyMemBackend sAllocator;                /* Global low level memory allocator */
  7000  #if defined(JX9_ENABLE_THREADS)
  7001  	const SyMutexMethods *pMutexMethods;   /* Mutex methods */
  7002  	SyMutex *pMutex;                       /* Global mutex */
  7003  	sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded 
  7004  										    * The threading level can be set using the [jx9_lib_config()]
  7005  											* interface with a configuration verb set to
  7006  											* JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or 
  7007  											* JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
  7008  											*/
  7009  #endif
  7010  	const jx9_vfs *pVfs;                    /* Underlying virtual file system */
  7011  	sxi32 nEngine;                          /* Total number of active engines */
  7012  	jx9 *pEngines;                          /* List of active engine */
  7013  	sxu32 nMagic;                           /* Sanity check against library misuse */
  7014  }sJx9MPGlobal = {
  7015  	{0, 0, 0, 0, 0, 0, 0, 0, {0}}, 
  7016  #if defined(JX9_ENABLE_THREADS)
  7017  	0, 
  7018  	0, 
  7019  	0, 
  7020  #endif
  7021  	0, 
  7022  	0, 
  7023  	0, 
  7024  	0
  7025  };
  7026  #define JX9_LIB_MAGIC  0xEA1495BA
  7027  #define JX9_LIB_MISUSE (sJx9MPGlobal.nMagic != JX9_LIB_MAGIC)
  7028  /*
  7029   * Supported threading level.
  7030   * These options have meaning only when the library is compiled with multi-threading
  7031   * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined
  7032   * when JX9 is built.
  7033   * JX9_THREAD_LEVEL_SINGLE:
  7034   * In this mode, mutexing is disabled and the library can only be used by a single thread.
  7035   * JX9_THREAD_LEVEL_MULTI
  7036   * In this mode, all mutexes including the recursive mutexes on [jx9] objects
  7037   * are enabled so that the application is free to share the same engine
  7038   * between different threads at the same time.
  7039   */
  7040  #define JX9_THREAD_LEVEL_SINGLE 1 
  7041  #define JX9_THREAD_LEVEL_MULTI  2
  7042  /*
  7043   * Configure a running JX9 engine instance.
  7044   * return JX9_OK on success.Any other return
  7045   * value indicates failure.
  7046   * Refer to [jx9_config()].
  7047   */
  7048  JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap)
  7049  {
  7050  	jx9_conf *pConf = &pEngine->xConf;
  7051  	int rc = JX9_OK;
  7052  	/* Perform the requested operation */
  7053  	switch(nOp){									 
  7054  	case JX9_CONFIG_ERR_LOG:{
  7055  		/* Extract compile-time error log if any */
  7056  		const char **pzPtr = va_arg(ap, const char **);
  7057  		int *pLen = va_arg(ap, int *);
  7058  		if( pzPtr == 0 ){
  7059  			rc = JX9_CORRUPT;
  7060  			break;
  7061  		}
  7062  		/* NULL terminate the error-log buffer */
  7063  		SyBlobNullAppend(&pConf->sErrConsumer);
  7064  		/* Point to the error-log buffer */
  7065  		*pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer);
  7066  		if( pLen ){
  7067  			if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){
  7068  				*pLen = (int)SyBlobLength(&pConf->sErrConsumer);
  7069  			}else{
  7070  				*pLen = 0;
  7071  			}
  7072  		}
  7073  		break;
  7074  							}
  7075  	case JX9_CONFIG_ERR_ABORT:
  7076  		/* Reserved for future use */
  7077  		break;
  7078  	default:
  7079  		/* Unknown configuration verb */
  7080  		rc = JX9_CORRUPT;
  7081  		break;
  7082  	} /* Switch() */
  7083  	return rc;
  7084  }
  7085  /*
  7086   * Configure the JX9 library.
  7087   * Return JX9_OK on success. Any other return value indicates failure.
  7088   * Refer to [jx9_lib_config()].
  7089   */
  7090  static sxi32 Jx9CoreConfigure(sxi32 nOp, va_list ap)
  7091  {
  7092  	int rc = JX9_OK;
  7093  	switch(nOp){
  7094  	    case JX9_LIB_CONFIG_VFS:{
  7095  			/* Install a virtual file system */
  7096  			const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *);
  7097  			sJx9MPGlobal.pVfs = pVfs;
  7098  			break;
  7099  								}
  7100  		case JX9_LIB_CONFIG_USER_MALLOC: {
  7101  			/* Use an alternative low-level memory allocation routines */
  7102  			const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
  7103  			/* Save the memory failure callback (if available) */
  7104  			ProcMemError xMemErr = sJx9MPGlobal.sAllocator.xMemError;
  7105  			void *pMemErr = sJx9MPGlobal.sAllocator.pUserData;
  7106  			if( pMethods == 0 ){
  7107  				/* Use the built-in memory allocation subsystem */
  7108  				rc = SyMemBackendInit(&sJx9MPGlobal.sAllocator, xMemErr, pMemErr);
  7109  			}else{
  7110  				rc = SyMemBackendInitFromOthers(&sJx9MPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
  7111  			}
  7112  			break;
  7113  										  }
  7114  		case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: {
  7115  			/* Memory failure callback */
  7116  			ProcMemError xMemErr = va_arg(ap, ProcMemError);
  7117  			void *pUserData = va_arg(ap, void *);
  7118  			sJx9MPGlobal.sAllocator.xMemError = xMemErr;
  7119  			sJx9MPGlobal.sAllocator.pUserData = pUserData;
  7120  			break;
  7121  												 }	  
  7122  		case JX9_LIB_CONFIG_USER_MUTEX: {
  7123  #if defined(JX9_ENABLE_THREADS)
  7124  			/* Use an alternative low-level mutex subsystem */
  7125  			const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
  7126  #if defined (UNTRUST)
  7127  			if( pMethods == 0 ){
  7128  				rc = JX9_CORRUPT;
  7129  			}
  7130  #endif
  7131  			/* Sanity check */
  7132  			if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
  7133  				/* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
  7134  				rc = JX9_CORRUPT;
  7135  				break;
  7136  			}
  7137  			if( sJx9MPGlobal.pMutexMethods ){
  7138  				/* Overwrite the previous mutex subsystem */
  7139  				SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
  7140  				if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
  7141  					sJx9MPGlobal.pMutexMethods->xGlobalRelease();
  7142  				}
  7143  				sJx9MPGlobal.pMutex = 0;
  7144  			}
  7145  			/* Initialize and install the new mutex subsystem */
  7146  			if( pMethods->xGlobalInit ){
  7147  				rc = pMethods->xGlobalInit();
  7148  				if ( rc != JX9_OK ){
  7149  					break;
  7150  				}
  7151  			}
  7152  			/* Create the global mutex */
  7153  			sJx9MPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
  7154  			if( sJx9MPGlobal.pMutex == 0 ){
  7155  				/*
  7156  				 * If the supplied mutex subsystem is so sick that we are unable to
  7157  				 * create a single mutex, there is no much we can do here.
  7158  				 */
  7159  				if( pMethods->xGlobalRelease ){
  7160  					pMethods->xGlobalRelease();
  7161  				}
  7162  				rc = JX9_CORRUPT;
  7163  				break;
  7164  			}
  7165  			sJx9MPGlobal.pMutexMethods = pMethods;			
  7166  			if( sJx9MPGlobal.nThreadingLevel == 0 ){
  7167  				/* Set a default threading level */
  7168  				sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI; 
  7169  			}
  7170  #endif
  7171  			break;
  7172  										   }
  7173  		case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE:
  7174  #if defined(JX9_ENABLE_THREADS)
  7175  			/* Single thread mode(Only one thread is allowed to play with the library) */
  7176  			sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE;
  7177  #endif
  7178  			break;
  7179  		case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI:
  7180  #if defined(JX9_ENABLE_THREADS)
  7181  			/* Multi-threading mode (library is thread safe and JX9 engines and virtual machines
  7182  			 * may be shared between multiple threads).
  7183  			 */
  7184  			sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
  7185  #endif
  7186  			break;
  7187  		default:
  7188  			/* Unknown configuration option */
  7189  			rc = JX9_CORRUPT;
  7190  			break;
  7191  	}
  7192  	return rc;
  7193  }
  7194  /*
  7195   * [CAPIREF: jx9_lib_config()]
  7196   * Please refer to the official documentation for function purpose and expected parameters.
  7197   */
  7198  JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...)
  7199  {
  7200  	va_list ap;
  7201  	int rc;
  7202  	if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
  7203  		/* Library is already initialized, this operation is forbidden */
  7204  		return JX9_LOOKED;
  7205  	}
  7206  	va_start(ap, nConfigOp);
  7207  	rc = Jx9CoreConfigure(nConfigOp, ap);
  7208  	va_end(ap);
  7209  	return rc;
  7210  }
  7211  /*
  7212   * Global library initialization
  7213   * Refer to [jx9_lib_init()]
  7214   * This routine must be called to initialize the memory allocation subsystem, the mutex 
  7215   * subsystem prior to doing any serious work with the library.The first thread to call
  7216   * this routine does the initialization process and set the magic number so no body later
  7217   * can re-initialize the library.If subsequent threads call this  routine before the first
  7218   * thread have finished the initialization process, then the subsequent threads must block 
  7219   * until the initialization process is done.
  7220   */
  7221  static sxi32 Jx9CoreInitialize(void)
  7222  {
  7223  	const jx9_vfs *pVfs; /* Built-in vfs */
  7224  #if defined(JX9_ENABLE_THREADS)
  7225  	const SyMutexMethods *pMutexMethods = 0;
  7226  	SyMutex *pMaster = 0;
  7227  #endif
  7228  	int rc;
  7229  	/*
  7230  	 * If the library is already initialized, then a call to this routine
  7231  	 * is a no-op.
  7232  	 */
  7233  	if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
  7234  		return JX9_OK; /* Already initialized */
  7235  	}
  7236  	/* Point to the built-in vfs */
  7237  	pVfs = jx9ExportBuiltinVfs();
  7238  	/* Install it */
  7239  	jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs);
  7240  #if defined(JX9_ENABLE_THREADS)
  7241  	if( sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){
  7242  		pMutexMethods = sJx9MPGlobal.pMutexMethods;
  7243  		if( pMutexMethods == 0 ){
  7244  			/* Use the built-in mutex subsystem */
  7245  			pMutexMethods = SyMutexExportMethods();
  7246  			if( pMutexMethods == 0 ){
  7247  				return JX9_CORRUPT; /* Can't happen */
  7248  			}
  7249  			/* Install the mutex subsystem */
  7250  			rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods);
  7251  			if( rc != JX9_OK ){
  7252  				return rc;
  7253  			}
  7254  		}
  7255  		/* Obtain a static mutex so we can initialize the library without calling malloc() */
  7256  		pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
  7257  		if( pMaster == 0 ){
  7258  			return JX9_CORRUPT; /* Can't happen */
  7259  		}
  7260  	}
  7261  	/* Lock the master mutex */
  7262  	rc = JX9_OK;
  7263  	SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7264  	if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
  7265  #endif
  7266  		if( sJx9MPGlobal.sAllocator.pMethods == 0 ){
  7267  			/* Install a memory subsystem */
  7268  			rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
  7269  			if( rc != JX9_OK ){
  7270  				/* If we are unable to initialize the memory backend, there is no much we can do here.*/
  7271  				goto End;
  7272  			}
  7273  		}
  7274  #if defined(JX9_ENABLE_THREADS)
  7275  		if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  7276  			/* Protect the memory allocation subsystem */
  7277  			rc = SyMemBackendMakeThreadSafe(&sJx9MPGlobal.sAllocator, sJx9MPGlobal.pMutexMethods);
  7278  			if( rc != JX9_OK ){
  7279  				goto End;
  7280  			}
  7281  		}
  7282  #endif
  7283  		/* Our library is initialized, set the magic number */
  7284  		sJx9MPGlobal.nMagic = JX9_LIB_MAGIC;
  7285  		rc = JX9_OK;
  7286  #if defined(JX9_ENABLE_THREADS)
  7287  	} /* sJx9MPGlobal.nMagic != JX9_LIB_MAGIC */
  7288  #endif
  7289  End:
  7290  #if defined(JX9_ENABLE_THREADS)
  7291  	/* Unlock the master mutex */
  7292  	SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7293  #endif
  7294  	return rc;
  7295  }
  7296  /*
  7297   * Release an active JX9 engine and it's associated active virtual machines.
  7298   */
  7299  static sxi32 EngineRelease(jx9 *pEngine)
  7300  {
  7301  	jx9_vm *pVm, *pNext;
  7302  	/* Release all active VM */
  7303  	pVm = pEngine->pVms;
  7304  	for(;;){
  7305  		if( pEngine->iVm < 1 ){
  7306  			break;
  7307  		}
  7308  		pNext = pVm->pNext;
  7309  		jx9VmRelease(pVm);
  7310  		pVm = pNext;
  7311  		pEngine->iVm--;
  7312  	}
  7313  	/* Set a dummy magic number */
  7314  	pEngine->nMagic = 0x7635;
  7315  	/* Release the private memory subsystem */
  7316  	SyMemBackendRelease(&pEngine->sAllocator); 
  7317  	return JX9_OK;
  7318  }
  7319  /*
  7320   * Release all resources consumed by the library.
  7321   * If JX9 is already shut when this routine is invoked then this
  7322   * routine is a harmless no-op.
  7323   * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()].
  7324   */
  7325  static void JX9CoreShutdown(void)
  7326  {
  7327  	jx9 *pEngine, *pNext;
  7328  	/* Release all active engines first */
  7329  	pEngine = sJx9MPGlobal.pEngines;
  7330  	for(;;){
  7331  		if( sJx9MPGlobal.nEngine < 1 ){
  7332  			break;
  7333  		}
  7334  		pNext = pEngine->pNext;
  7335  		EngineRelease(pEngine); 
  7336  		pEngine = pNext;
  7337  		sJx9MPGlobal.nEngine--;
  7338  	}
  7339  #if defined(JX9_ENABLE_THREADS)
  7340  	/* Release the mutex subsystem */
  7341  	if( sJx9MPGlobal.pMutexMethods ){
  7342  		if( sJx9MPGlobal.pMutex ){
  7343  			SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
  7344  			sJx9MPGlobal.pMutex = 0;
  7345  		}
  7346  		if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
  7347  			sJx9MPGlobal.pMutexMethods->xGlobalRelease();
  7348  		}
  7349  		sJx9MPGlobal.pMutexMethods = 0;
  7350  	}
  7351  	sJx9MPGlobal.nThreadingLevel = 0;
  7352  #endif
  7353  	if( sJx9MPGlobal.sAllocator.pMethods ){
  7354  		/* Release the memory backend */
  7355  		SyMemBackendRelease(&sJx9MPGlobal.sAllocator);
  7356  	}
  7357  	sJx9MPGlobal.nMagic = 0x1928;	
  7358  }
  7359  /*
  7360   * [CAPIREF: jx9_lib_shutdown()]
  7361   * Please refer to the official documentation for function purpose and expected parameters.
  7362   */
  7363  JX9_PRIVATE int jx9_lib_shutdown(void)
  7364  {
  7365  	if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
  7366  		/* Already shut */
  7367  		return JX9_OK;
  7368  	}
  7369  	JX9CoreShutdown();
  7370  	return JX9_OK;
  7371  }
  7372  /*
  7373   * [CAPIREF: jx9_lib_signature()]
  7374   * Please refer to the official documentation for function purpose and expected parameters.
  7375   */
  7376  JX9_PRIVATE const char * jx9_lib_signature(void)
  7377  {
  7378  	return JX9_SIG;
  7379  }
  7380  /*
  7381   * [CAPIREF: jx9_init()]
  7382   * Please refer to the official documentation for function purpose and expected parameters.
  7383   */
  7384  JX9_PRIVATE int jx9_init(jx9 **ppEngine)
  7385  {
  7386  	jx9 *pEngine;
  7387  	int rc;
  7388  #if defined(UNTRUST)
  7389  	if( ppEngine == 0 ){
  7390  		return JX9_CORRUPT;
  7391  	}
  7392  #endif
  7393  	*ppEngine = 0;
  7394  	/* One-time automatic library initialization */
  7395  	rc = Jx9CoreInitialize();
  7396  	if( rc != JX9_OK ){
  7397  		return rc;
  7398  	}
  7399  	/* Allocate a new engine */
  7400  	pEngine = (jx9 *)SyMemBackendPoolAlloc(&sJx9MPGlobal.sAllocator, sizeof(jx9));
  7401  	if( pEngine == 0 ){
  7402  		return JX9_NOMEM;
  7403  	}
  7404  	/* Zero the structure */
  7405  	SyZero(pEngine, sizeof(jx9));
  7406  	/* Initialize engine fields */
  7407  	pEngine->nMagic = JX9_ENGINE_MAGIC;
  7408  	rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sJx9MPGlobal.sAllocator);
  7409  	if( rc != JX9_OK ){
  7410  		goto Release;
  7411  	}
  7412  #if defined(JX9_ENABLE_THREADS)
  7413  	SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
  7414  #endif
  7415  	/* Default configuration */
  7416  	SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
  7417  	/* Install a default compile-time error consumer routine */
  7418  	pEngine->xConf.xErr = jx9VmBlobConsumer;
  7419  	pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer;
  7420  	/* Built-in vfs */
  7421  	pEngine->pVfs = sJx9MPGlobal.pVfs;
  7422  #if defined(JX9_ENABLE_THREADS)
  7423  	if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  7424  		 /* Associate a recursive mutex with this instance */
  7425  		 pEngine->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  7426  		 if( pEngine->pMutex == 0 ){
  7427  			 rc = JX9_NOMEM;
  7428  			 goto Release;
  7429  		 }
  7430  	 }
  7431  #endif
  7432  	/* Link to the list of active engines */
  7433  #if defined(JX9_ENABLE_THREADS)
  7434  	/* Enter the global mutex */
  7435  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7436  #endif
  7437  	MACRO_LD_PUSH(sJx9MPGlobal.pEngines, pEngine);
  7438  	sJx9MPGlobal.nEngine++;
  7439  #if defined(JX9_ENABLE_THREADS)
  7440  	/* Leave the global mutex */
  7441  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7442  #endif
  7443  	/* Write a pointer to the new instance */
  7444  	*ppEngine = pEngine;
  7445  	return JX9_OK;
  7446  Release:
  7447  	SyMemBackendRelease(&pEngine->sAllocator);
  7448  	SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator,pEngine);
  7449  	return rc;
  7450  }
  7451  /*
  7452   * [CAPIREF: jx9_release()]
  7453   * Please refer to the official documentation for function purpose and expected parameters.
  7454   */
  7455  JX9_PRIVATE int jx9_release(jx9 *pEngine)
  7456  {
  7457  	int rc;
  7458  	if( JX9_ENGINE_MISUSE(pEngine) ){
  7459  		return JX9_CORRUPT;
  7460  	}
  7461  #if defined(JX9_ENABLE_THREADS)
  7462  	 /* Acquire engine mutex */
  7463  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7464  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7465  		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
  7466  			 return JX9_ABORT; /* Another thread have released this instance */
  7467  	 }
  7468  #endif
  7469  	/* Release the engine */
  7470  	rc = EngineRelease(&(*pEngine));
  7471  #if defined(JX9_ENABLE_THREADS)
  7472  	 /* Leave engine mutex */
  7473  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7474  	 /* Release engine mutex */
  7475  	 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7476  #endif
  7477  #if defined(JX9_ENABLE_THREADS)
  7478  	/* Enter the global mutex */
  7479  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7480  #endif
  7481  	/* Unlink from the list of active engines */
  7482  	MACRO_LD_REMOVE(sJx9MPGlobal.pEngines, pEngine);
  7483  	sJx9MPGlobal.nEngine--;
  7484  #if defined(JX9_ENABLE_THREADS)
  7485  	/* Leave the global mutex */
  7486  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  7487  #endif
  7488  	/* Release the memory chunk allocated to this engine */
  7489  	SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator, pEngine);
  7490  	return rc;
  7491  }
  7492  /*
  7493   * Compile a raw JX9 script.
  7494   * To execute a JX9 code, it must first be compiled into a bytecode program using this routine.
  7495   * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback]
  7496   * should  display the appropriate error message and this function set ppVm to null and return
  7497   * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled
  7498   * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.].
  7499   * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script
  7500   * for evaluation.
  7501   */
  7502  static sxi32 ProcessScript(
  7503  	jx9 *pEngine,          /* Running JX9 engine */
  7504  	jx9_vm **ppVm,         /* OUT: A pointer to the virtual machine */
  7505  	SyString *pScript,     /* Raw JX9 script to compile */
  7506  	sxi32 iFlags,          /* Compile-time flags */
  7507  	const char *zFilePath  /* File path if script come from a file. NULL otherwise */
  7508  	)
  7509  {
  7510  	jx9_vm *pVm;
  7511  	int rc;
  7512  	/* Allocate a new virtual machine */
  7513  	pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm));
  7514  	if( pVm == 0 ){
  7515  		/* If the supplied memory subsystem is so sick that we are unable to allocate
  7516  		 * a tiny chunk of memory, there is no much we can do here. */
  7517  		if( ppVm ){
  7518  			*ppVm = 0;
  7519  		}
  7520  		return JX9_NOMEM;
  7521  	}
  7522  	if( iFlags < 0 ){
  7523  		/* Default compile-time flags */
  7524  		iFlags = 0;
  7525  	}
  7526  	/* Initialize the Virtual Machine */
  7527  	rc = jx9VmInit(pVm, &(*pEngine));
  7528  	if( rc != JX9_OK ){
  7529  		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  7530  		if( ppVm ){
  7531  			*ppVm = 0;
  7532  		}
  7533  		return JX9_VM_ERR;
  7534  	}
  7535  	if( zFilePath ){
  7536  		/* Push processed file path */
  7537  		jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0);
  7538  	}
  7539  	/* Reset the error message consumer */
  7540  	SyBlobReset(&pEngine->xConf.sErrConsumer);
  7541  	/* Compile the script */
  7542  	jx9CompileScript(pVm, &(*pScript), iFlags);
  7543  	if( pVm->sCodeGen.nErr > 0 || pVm == 0){
  7544  		sxu32 nErr = pVm->sCodeGen.nErr;
  7545  		/* Compilation error or null ppVm pointer, release this VM */
  7546  		SyMemBackendRelease(&pVm->sAllocator);
  7547  		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  7548  		if( ppVm ){
  7549  			*ppVm = 0;
  7550  		}
  7551  		return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK;
  7552  	}
  7553  	/* Prepare the virtual machine for bytecode execution */
  7554  	rc = jx9VmMakeReady(pVm);
  7555  	if( rc != JX9_OK ){
  7556  		goto Release;
  7557  	}
  7558  	/* Install local import path which is the current directory */
  7559  	jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./");
  7560  #if defined(JX9_ENABLE_THREADS)
  7561  	if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  7562  		 /* Associate a recursive mutex with this instance */
  7563  		 pVm->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  7564  		 if( pVm->pMutex == 0 ){
  7565  			 goto Release;
  7566  		 }
  7567  	 }
  7568  #endif
  7569  	/* Script successfully compiled, link to the list of active virtual machines */
  7570  	MACRO_LD_PUSH(pEngine->pVms, pVm);
  7571  	pEngine->iVm++;
  7572  	/* Point to the freshly created VM */
  7573  	*ppVm = pVm;
  7574  	/* Ready to execute JX9 bytecode */
  7575  	return JX9_OK;
  7576  Release:
  7577  	SyMemBackendRelease(&pVm->sAllocator);
  7578  	SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  7579  	*ppVm = 0;
  7580  	return JX9_VM_ERR;
  7581  }
  7582  /*
  7583   * [CAPIREF: jx9_compile()]
  7584   * Please refer to the official documentation for function purpose and expected parameters.
  7585   */
  7586  JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm)
  7587  {
  7588  	SyString sScript;
  7589  	int rc;
  7590  	if( JX9_ENGINE_MISUSE(pEngine) ){
  7591  		return JX9_CORRUPT;
  7592  	}
  7593  	if( zSource == 0 ){
  7594  		/* Empty Jx9 statement ';' */
  7595  		zSource = ";";
  7596  		nLen = (int)sizeof(char);
  7597  	}
  7598  	if( nLen < 0 ){
  7599  		/* Compute input length automatically */
  7600  		nLen = (int)SyStrlen(zSource);
  7601  	}
  7602  	SyStringInitFromBuf(&sScript, zSource, nLen);
  7603  #if defined(JX9_ENABLE_THREADS)
  7604  	 /* Acquire engine mutex */
  7605  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7606  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7607  		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
  7608  			 return JX9_ABORT; /* Another thread have released this instance */
  7609  	 }
  7610  #endif
  7611  	/* Compile the script */
  7612  	rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0);
  7613  #if defined(JX9_ENABLE_THREADS)
  7614  	 /* Leave engine mutex */
  7615  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7616  #endif
  7617  	/* Compilation result */
  7618  	return rc;
  7619  }
  7620  /*
  7621   * [CAPIREF: jx9_compile_file()]
  7622   * Please refer to the official documentation for function purpose and expected parameters.
  7623   */
  7624  JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm)
  7625  {
  7626  	const jx9_vfs *pVfs;
  7627  	int rc;
  7628  	if( ppOutVm ){
  7629  		*ppOutVm = 0;
  7630  	}
  7631  	rc = JX9_OK; /* cc warning */
  7632  	if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){
  7633  		return JX9_CORRUPT;
  7634  	}
  7635  #if defined(JX9_ENABLE_THREADS)
  7636  	 /* Acquire engine mutex */
  7637  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7638  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7639  		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
  7640  			 return JX9_ABORT; /* Another thread have released this instance */
  7641  	 }
  7642  #endif
  7643  	 /*
  7644  	  * Check if the underlying vfs implement the memory map
  7645  	  * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
  7646  	  */
  7647  	 pVfs = pEngine->pVfs;
  7648  	 if( pVfs == 0 || pVfs->xMmap == 0 ){
  7649  		 /* Memory map routine not implemented */
  7650  		 rc = JX9_IO_ERR;
  7651  	 }else{
  7652  		 void *pMapView = 0; /* cc warning */
  7653  		 jx9_int64 nSize = 0; /* cc warning */
  7654  		 SyString sScript;
  7655  		 /* Try to get a memory view of the whole file */
  7656  		 rc = pVfs->xMmap(zFilePath, &pMapView, &nSize);
  7657  		 if( rc != JX9_OK ){
  7658  			 /* Assume an IO error */
  7659  			 rc = JX9_IO_ERR;
  7660  		 }else{
  7661  			 /* Compile the file */
  7662  			 SyStringInitFromBuf(&sScript, pMapView, nSize);
  7663  			 rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath);
  7664  			 /* Release the memory view of the whole file */
  7665  			 if( pVfs->xUnmap ){
  7666  				 pVfs->xUnmap(pMapView, nSize);
  7667  			 }
  7668  		 }
  7669  	 }
  7670  #if defined(JX9_ENABLE_THREADS)
  7671  	 /* Leave engine mutex */
  7672  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7673  #endif
  7674  	/* Compilation result */
  7675  	return rc;
  7676  }
  7677  /*
  7678   * [CAPIREF: jx9_vm_config()]
  7679   * Please refer to the official documentation for function purpose and expected parameters.
  7680   */
  7681  JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...)
  7682  {
  7683  	va_list ap;
  7684  	int rc;
  7685  	/* Ticket 1433-002: NULL VM is harmless operation */
  7686  	if ( JX9_VM_MISUSE(pVm) ){
  7687  		return JX9_CORRUPT;
  7688  	}
  7689  #if defined(JX9_ENABLE_THREADS)
  7690  	 /* Acquire VM mutex */
  7691  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7692  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7693  		 JX9_THRD_VM_RELEASE(pVm) ){
  7694  			 return JX9_ABORT; /* Another thread have released this instance */
  7695  	 }
  7696  #endif
  7697  	/* Confiugure the virtual machine */
  7698  	va_start(ap, iConfigOp);
  7699  	rc = jx9VmConfigure(&(*pVm), iConfigOp, ap);
  7700  	va_end(ap);
  7701  #if defined(JX9_ENABLE_THREADS)
  7702  	 /* Leave VM mutex */
  7703  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7704  #endif
  7705  	return rc;
  7706  }
  7707  /*
  7708   * [CAPIREF: jx9_vm_release()]
  7709   * Please refer to the official documentation for function purpose and expected parameters.
  7710   */
  7711  JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm)
  7712  {
  7713  	jx9 *pEngine;
  7714  	int rc;
  7715  	/* Ticket 1433-002: NULL VM is harmless operation */
  7716  	if ( JX9_VM_MISUSE(pVm) ){
  7717  		return JX9_CORRUPT;
  7718  	}
  7719  #if defined(JX9_ENABLE_THREADS)
  7720  	 /* Acquire VM mutex */
  7721  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7722  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7723  		 JX9_THRD_VM_RELEASE(pVm) ){
  7724  			 return JX9_ABORT; /* Another thread have released this instance */
  7725  	 }
  7726  #endif
  7727  	pEngine = pVm->pEngine;
  7728  	rc = jx9VmRelease(&(*pVm));
  7729  #if defined(JX9_ENABLE_THREADS)
  7730  	 /* Leave VM mutex */
  7731  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7732  	 /* Release VM mutex */
  7733  	 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pVm->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7734  #endif
  7735  	if( rc == JX9_OK ){
  7736  		/* Unlink from the list of active VM */
  7737  #if defined(JX9_ENABLE_THREADS)
  7738  			/* Acquire engine mutex */
  7739  			SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7740  			if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7741  				JX9_THRD_ENGINE_RELEASE(pEngine) ){
  7742  					return JX9_ABORT; /* Another thread have released this instance */
  7743  			}
  7744  #endif
  7745  		MACRO_LD_REMOVE(pEngine->pVms, pVm);
  7746  		pEngine->iVm--;
  7747  		/* Release the memory chunk allocated to this VM */
  7748  		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  7749  #if defined(JX9_ENABLE_THREADS)
  7750  			/* Leave engine mutex */
  7751  			SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7752  #endif	
  7753  	}
  7754  	return rc;
  7755  }
  7756  /*
  7757   * [CAPIREF: jx9_create_function()]
  7758   * Please refer to the official documentation for function purpose and expected parameters.
  7759   */
  7760  JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData)
  7761  {
  7762  	SyString sName;
  7763  	int rc;
  7764  	/* Ticket 1433-002: NULL VM is harmless operation */
  7765  	if ( JX9_VM_MISUSE(pVm) ){
  7766  		return JX9_CORRUPT;
  7767  	}
  7768  	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  7769  	/* Remove leading and trailing white spaces */
  7770  	SyStringFullTrim(&sName);
  7771  	/* Ticket 1433-003: NULL values are not allowed */
  7772  	if( sName.nByte < 1 || xFunc == 0 ){
  7773  		return JX9_CORRUPT;
  7774  	}
  7775  #if defined(JX9_ENABLE_THREADS)
  7776  	 /* Acquire VM mutex */
  7777  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7778  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7779  		 JX9_THRD_VM_RELEASE(pVm) ){
  7780  			 return JX9_ABORT; /* Another thread have released this instance */
  7781  	 }
  7782  #endif
  7783  	/* Install the foreign function */
  7784  	rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData); 
  7785  #if defined(JX9_ENABLE_THREADS)
  7786  	 /* Leave VM mutex */
  7787  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7788  #endif
  7789  	return rc;
  7790  }
  7791  JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName)
  7792  {
  7793  	jx9_user_func *pFunc = 0; /* cc warning */
  7794  	int rc;
  7795  	/* Perform the deletion */
  7796  	rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
  7797  	if( rc == JX9_OK ){
  7798  		/* Release internal fields */
  7799  		SySetRelease(&pFunc->aAux);
  7800  		SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
  7801  		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
  7802  	}
  7803  	return rc;
  7804  }
  7805  /*
  7806   * [CAPIREF: jx9_create_constant()]
  7807   * Please refer to the official documentation for function purpose and expected parameters.
  7808   */
  7809  JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData)
  7810  {
  7811  	SyString sName;
  7812  	int rc;
  7813  	/* Ticket 1433-002: NULL VM is harmless operation */
  7814  	if ( JX9_VM_MISUSE(pVm) ){
  7815  		return JX9_CORRUPT;
  7816  	}
  7817  	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  7818  	/* Remove leading and trailing white spaces */
  7819  	SyStringFullTrim(&sName);
  7820  	if( sName.nByte < 1 ){
  7821  		/* Empty constant name */
  7822  		return JX9_CORRUPT;
  7823  	}
  7824  	/* TICKET 1433-003: NULL pointer is harmless operation */
  7825  	if( xExpand == 0 ){
  7826  		return JX9_CORRUPT;
  7827  	}
  7828  #if defined(JX9_ENABLE_THREADS)
  7829  	 /* Acquire VM mutex */
  7830  	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7831  	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
  7832  		 JX9_THRD_VM_RELEASE(pVm) ){
  7833  			 return JX9_ABORT; /* Another thread have released this instance */
  7834  	 }
  7835  #endif
  7836  	/* Perform the registration */
  7837  	rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData);
  7838  #if defined(JX9_ENABLE_THREADS)
  7839  	 /* Leave VM mutex */
  7840  	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  7841  #endif
  7842  	 return rc;
  7843  }
  7844  JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName)
  7845  {
  7846  	jx9_constant *pCons;
  7847  	int rc;
  7848  	/* Query the constant hashtable */
  7849  	 rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
  7850  	 if( rc == JX9_OK ){
  7851  		 /* Perform the deletion */
  7852  		 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
  7853  		 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
  7854  	 }
  7855  	 return rc;
  7856  }
  7857  /*
  7858   * [CAPIREF: jx9_new_scalar()]
  7859   * Please refer to the official documentation for function purpose and expected parameters.
  7860   */
  7861  JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm)
  7862  {
  7863  	jx9_value *pObj;
  7864  	/* Ticket 1433-002: NULL VM is harmless operation */
  7865  	if ( JX9_VM_MISUSE(pVm) ){
  7866  		return 0;
  7867  	}
  7868  	/* Allocate a new scalar variable */
  7869  	pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
  7870  	if( pObj == 0 ){
  7871  		return 0;
  7872  	}
  7873  	/* Nullify the new scalar */
  7874  	jx9MemObjInit(pVm, pObj);
  7875  	return pObj;
  7876  }
  7877  /*
  7878   * [CAPIREF: jx9_new_array()] 
  7879   * Please refer to the official documentation for function purpose and expected parameters.
  7880   */
  7881  JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm)
  7882  {
  7883  	jx9_hashmap *pMap;
  7884  	jx9_value *pObj;
  7885  	/* Ticket 1433-002: NULL VM is harmless operation */
  7886  	if ( JX9_VM_MISUSE(pVm) ){
  7887  		return 0;
  7888  	}
  7889  	/* Create a new hashmap first */
  7890  	pMap = jx9NewHashmap(&(*pVm), 0, 0);
  7891  	if( pMap == 0 ){
  7892  		return 0;
  7893  	}
  7894  	/* Associate a new jx9_value with this hashmap */
  7895  	pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
  7896  	if( pObj == 0 ){
  7897  		jx9HashmapRelease(pMap, TRUE);
  7898  		return 0;
  7899  	}
  7900  	jx9MemObjInitFromArray(pVm, pObj, pMap);
  7901  	return pObj;
  7902  }
  7903  /*
  7904   * [CAPIREF: jx9_release_value()]
  7905   * Please refer to the official documentation for function purpose and expected parameters.
  7906   */
  7907  JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue)
  7908  {
  7909  	/* Ticket 1433-002: NULL VM is a harmless operation */
  7910  	if ( JX9_VM_MISUSE(pVm) ){
  7911  		return JX9_CORRUPT;
  7912  	}
  7913  	if( pValue ){
  7914  		/* Release the value */
  7915  		jx9MemObjRelease(pValue);
  7916  		SyMemBackendPoolFree(&pVm->sAllocator, pValue);
  7917  	}
  7918  	return JX9_OK;
  7919  }
  7920  /*
  7921   * [CAPIREF: jx9_value_to_int()]
  7922   * Please refer to the official documentation for function purpose and expected parameters.
  7923   */
  7924  JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue)
  7925  {
  7926  	int rc;
  7927  	rc = jx9MemObjToInteger(pValue);
  7928  	if( rc != JX9_OK ){
  7929  		return 0;
  7930  	}
  7931  	return (int)pValue->x.iVal;
  7932  }
  7933  /*
  7934   * [CAPIREF: jx9_value_to_bool()]
  7935   * Please refer to the official documentation for function purpose and expected parameters.
  7936   */
  7937  JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue)
  7938  {
  7939  	int rc;
  7940  	rc = jx9MemObjToBool(pValue);
  7941  	if( rc != JX9_OK ){
  7942  		return 0;
  7943  	}
  7944  	return (int)pValue->x.iVal;
  7945  }
  7946  /*
  7947   * [CAPIREF: jx9_value_to_int64()]
  7948   * Please refer to the official documentation for function purpose and expected parameters.
  7949   */
  7950  JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue)
  7951  {
  7952  	int rc;
  7953  	rc = jx9MemObjToInteger(pValue);
  7954  	if( rc != JX9_OK ){
  7955  		return 0;
  7956  	}
  7957  	return pValue->x.iVal;
  7958  }
  7959  /*
  7960   * [CAPIREF: jx9_value_to_double()]
  7961   * Please refer to the official documentation for function purpose and expected parameters.
  7962   */
  7963  JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue)
  7964  {
  7965  	int rc;
  7966  	rc = jx9MemObjToReal(pValue);
  7967  	if( rc != JX9_OK ){
  7968  		return (double)0;
  7969  	}
  7970  	return (double)pValue->x.rVal;
  7971  }
  7972  /*
  7973   * [CAPIREF: jx9_value_to_string()]
  7974   * Please refer to the official documentation for function purpose and expected parameters.
  7975   */
  7976  JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen)
  7977  {
  7978  	jx9MemObjToString(pValue);
  7979  	if( SyBlobLength(&pValue->sBlob) > 0 ){
  7980  		SyBlobNullAppend(&pValue->sBlob);
  7981  		if( pLen ){
  7982  			*pLen = (int)SyBlobLength(&pValue->sBlob);
  7983  		}
  7984  		return (const char *)SyBlobData(&pValue->sBlob);
  7985  	}else{
  7986  		/* Return the empty string */
  7987  		if( pLen ){
  7988  			*pLen = 0;
  7989  		}
  7990  		return "";
  7991  	}
  7992  }
  7993  /*
  7994   * [CAPIREF: jx9_value_to_resource()]
  7995   * Please refer to the official documentation for function purpose and expected parameters.
  7996   */
  7997  JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue)
  7998  {
  7999  	if( (pValue->iFlags & MEMOBJ_RES) == 0 ){
  8000  		/* Not a resource, return NULL */
  8001  		return 0;
  8002  	}
  8003  	return pValue->x.pOther;
  8004  }
  8005  /*
  8006   * [CAPIREF: jx9_value_compare()]
  8007   * Please refer to the official documentation for function purpose and expected parameters.
  8008   */
  8009  JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict)
  8010  {
  8011  	int rc;
  8012  	if( pLeft == 0 || pRight == 0 ){
  8013  		/* TICKET 1433-24: NULL values is harmless operation */
  8014  		return 1;
  8015  	}
  8016  	/* Perform the comparison */
  8017  	rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0);
  8018  	/* Comparison result */
  8019  	return rc;
  8020  }
  8021  /*
  8022   * [CAPIREF: jx9_result_int()]
  8023   * Please refer to the official documentation for function purpose and expected parameters.
  8024   */
  8025  JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue)
  8026  {
  8027  	return jx9_value_int(pCtx->pRet, iValue);
  8028  }
  8029  /*
  8030   * [CAPIREF: jx9_result_int64()]
  8031   * Please refer to the official documentation for function purpose and expected parameters.
  8032   */
  8033  JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue)
  8034  {
  8035  	return jx9_value_int64(pCtx->pRet, iValue);
  8036  }
  8037  /*
  8038   * [CAPIREF: jx9_result_bool()]
  8039   * Please refer to the official documentation for function purpose and expected parameters.
  8040   */
  8041  JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool)
  8042  {
  8043  	return jx9_value_bool(pCtx->pRet, iBool);
  8044  }
  8045  /*
  8046   * [CAPIREF: jx9_result_double()]
  8047   * Please refer to the official documentation for function purpose and expected parameters.
  8048   */
  8049  JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value)
  8050  {
  8051  	return jx9_value_double(pCtx->pRet, Value);
  8052  }
  8053  /*
  8054   * [CAPIREF: jx9_result_null()]
  8055   * Please refer to the official documentation for function purpose and expected parameters.
  8056   */
  8057  JX9_PRIVATE int jx9_result_null(jx9_context *pCtx)
  8058  {
  8059  	/* Invalidate any prior representation and set the NULL flag */
  8060  	jx9MemObjRelease(pCtx->pRet);
  8061  	return JX9_OK;
  8062  }
  8063  /*
  8064   * [CAPIREF: jx9_result_string()]
  8065   * Please refer to the official documentation for function purpose and expected parameters.
  8066   */
  8067  JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen)
  8068  {
  8069  	return jx9_value_string(pCtx->pRet, zString, nLen);
  8070  }
  8071  /*
  8072   * [CAPIREF: jx9_result_string_format()]
  8073   * Please refer to the official documentation for function purpose and expected parameters.
  8074   */
  8075  JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...)
  8076  {
  8077  	jx9_value *p;
  8078  	va_list ap;
  8079  	int rc;
  8080  	p = pCtx->pRet;
  8081  	if( (p->iFlags & MEMOBJ_STRING) == 0 ){
  8082  		/* Invalidate any prior representation */
  8083  		jx9MemObjRelease(p);
  8084  		MemObjSetType(p, MEMOBJ_STRING);
  8085  	}
  8086  	/* Format the given string */
  8087  	va_start(ap, zFormat);
  8088  	rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
  8089  	va_end(ap);
  8090  	return rc;
  8091  }
  8092  /*
  8093   * [CAPIREF: jx9_result_value()]
  8094   * Please refer to the official documentation for function purpose and expected parameters.
  8095   */
  8096  JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue)
  8097  {
  8098  	int rc = JX9_OK;
  8099  	if( pValue == 0 ){
  8100  		jx9MemObjRelease(pCtx->pRet);
  8101  	}else{
  8102  		rc = jx9MemObjStore(pValue, pCtx->pRet);
  8103  	}
  8104  	return rc;
  8105  }
  8106  /*
  8107   * [CAPIREF: jx9_result_resource()]
  8108   * Please refer to the official documentation for function purpose and expected parameters.
  8109   */
  8110  JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData)
  8111  {
  8112  	return jx9_value_resource(pCtx->pRet, pUserData);
  8113  }
  8114  /*
  8115   * [CAPIREF: jx9_context_new_scalar()]
  8116   * Please refer to the official documentation for function purpose and expected parameters.
  8117   */
  8118  JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx)
  8119  {
  8120  	jx9_value *pVal;
  8121  	pVal = jx9_new_scalar(pCtx->pVm);
  8122  	if( pVal ){
  8123  		/* Record value address so it can be freed automatically
  8124  		 * when the calling function returns. 
  8125  		 */
  8126  		SySetPut(&pCtx->sVar, (const void *)&pVal);
  8127  	}
  8128  	return pVal;
  8129  }
  8130  /*
  8131   * [CAPIREF: jx9_context_new_array()]
  8132   * Please refer to the official documentation for function purpose and expected parameters.
  8133   */
  8134  JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx)
  8135  {
  8136  	jx9_value *pVal;
  8137  	pVal = jx9_new_array(pCtx->pVm);
  8138  	if( pVal ){
  8139  		/* Record value address so it can be freed automatically
  8140  		 * when the calling function returns. 
  8141  		 */
  8142  		SySetPut(&pCtx->sVar, (const void *)&pVal);
  8143  	}
  8144  	return pVal;
  8145  }
  8146  /*
  8147   * [CAPIREF: jx9_context_release_value()]
  8148   * Please refer to the official documentation for function purpose and expected parameters.
  8149   */
  8150  JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue)
  8151  {
  8152  	jx9VmReleaseContextValue(&(*pCtx), pValue);
  8153  }
  8154  /*
  8155   * [CAPIREF: jx9_context_alloc_chunk()]
  8156   * Please refer to the official documentation for function purpose and expected parameters.
  8157   */
  8158  JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease)
  8159  {
  8160  	void *pChunk;
  8161  	pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte);
  8162  	if( pChunk ){
  8163  		if( ZeroChunk ){
  8164  			/* Zero the memory chunk */
  8165  			SyZero(pChunk, nByte);
  8166  		}
  8167  		if( AutoRelease ){
  8168  			jx9_aux_data sAux;
  8169  			/* Track the chunk so that it can be released automatically 
  8170  			 * upon this context is destroyed.
  8171  			 */
  8172  			sAux.pAuxData = pChunk;
  8173  			SySetPut(&pCtx->sChunk, (const void *)&sAux);
  8174  		}
  8175  	}
  8176  	return pChunk;
  8177  }
  8178  /*
  8179   * Check if the given chunk address is registered in the call context
  8180   * chunk container.
  8181   * Return TRUE if registered.FALSE otherwise.
  8182   * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()].
  8183   */
  8184  static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk)
  8185  {
  8186  	jx9_aux_data *aAux, *pAux;
  8187  	sxu32 n;
  8188  	if( SySetUsed(&pCtx->sChunk) < 1 ){
  8189  		/* Don't bother processing, the container is empty */
  8190  		return 0;
  8191  	}
  8192  	/* Perform the lookup */
  8193  	aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
  8194  	for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
  8195  		pAux = &aAux[n];
  8196  		if( pAux->pAuxData == pChunk ){
  8197  			/* Chunk found */
  8198  			return pAux;
  8199  		}
  8200  	}
  8201  	/* No such allocated chunk */
  8202  	return 0;
  8203  }
  8204  /*
  8205   * [CAPIREF: jx9_context_realloc_chunk()]
  8206   * Please refer to the official documentation for function purpose and expected parameters.
  8207   */
  8208  JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte)
  8209  {
  8210  	jx9_aux_data *pAux;
  8211  	void *pNew;
  8212  	pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte);
  8213  	if( pNew ){
  8214  		pAux = ContextFindChunk(pCtx, pChunk);
  8215  		if( pAux ){
  8216  			pAux->pAuxData = pNew;
  8217  		}
  8218  	}
  8219  	return pNew;
  8220  }
  8221  /*
  8222   * [CAPIREF: jx9_context_free_chunk()]
  8223   * Please refer to the official documentation for function purpose and expected parameters.
  8224   */
  8225  JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk)
  8226  {
  8227  	jx9_aux_data *pAux;
  8228  	if( pChunk == 0 ){
  8229  		/* TICKET-1433-93: NULL chunk is a harmless operation */
  8230  		return;
  8231  	}
  8232  	pAux = ContextFindChunk(pCtx, pChunk);
  8233  	if( pAux ){
  8234  		/* Mark as destroyed */
  8235  		pAux->pAuxData = 0;
  8236  	}
  8237  	SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
  8238  }
  8239  /*
  8240   * [CAPIREF: jx9_array_fetch()]
  8241   * Please refer to the official documentation for function purpose and expected parameters.
  8242   */
  8243  JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte)
  8244  {
  8245  	jx9_hashmap_node *pNode;
  8246  	jx9_value *pValue;
  8247  	jx9_value skey;
  8248  	int rc;
  8249  	/* Make sure we are dealing with a valid hashmap */
  8250  	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8251  		return 0;
  8252  	}
  8253  	if( nByte < 0 ){
  8254  		nByte = (int)SyStrlen(zKey);
  8255  	}
  8256  	/* Convert the key to a jx9_value  */
  8257  	jx9MemObjInit(pArray->pVm, &skey);
  8258  	jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte);
  8259  	/* Perform the lookup */
  8260  	rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode);
  8261  	jx9MemObjRelease(&skey);
  8262  	if( rc != JX9_OK ){
  8263  		/* No such entry */
  8264  		return 0;
  8265  	}
  8266  	/* Extract the target value */
  8267  	pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx);
  8268  	return pValue;
  8269  }
  8270  /*
  8271   * [CAPIREF: jx9_array_walk()]
  8272   * Please refer to the official documentation for function purpose and expected parameters.
  8273   */
  8274  JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData)
  8275  {
  8276  	int rc;
  8277  	if( xWalk == 0 ){
  8278  		return JX9_CORRUPT;
  8279  	}
  8280  	/* Make sure we are dealing with a valid hashmap */
  8281  	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8282  		return JX9_CORRUPT;
  8283  	}
  8284  	/* Start the walk process */
  8285  	rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData);
  8286  	return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK;
  8287  }
  8288  /*
  8289   * [CAPIREF: jx9_array_add_elem()]
  8290   * Please refer to the official documentation for function purpose and expected parameters.
  8291   */
  8292  JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue)
  8293  {
  8294  	int rc;
  8295  	/* Make sure we are dealing with a valid hashmap */
  8296  	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8297  		return JX9_CORRUPT;
  8298  	}
  8299  	/* Perform the insertion */
  8300  	rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue));
  8301  	return rc;
  8302  }
  8303  /*
  8304   * [CAPIREF: jx9_array_add_strkey_elem()]
  8305   * Please refer to the official documentation for function purpose and expected parameters.
  8306   */
  8307  JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue)
  8308  {
  8309  	int rc;
  8310  	/* Make sure we are dealing with a valid hashmap */
  8311  	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8312  		return JX9_CORRUPT;
  8313  	}
  8314  	/* Perform the insertion */
  8315  	if( SX_EMPTY_STR(zKey) ){
  8316  		/* Empty key, assign an automatic index */
  8317  		rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue));
  8318  	}else{
  8319  		jx9_value sKey;
  8320  		jx9MemObjInitFromString(pArray->pVm, &sKey, 0);
  8321  		jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey));
  8322  		rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue));
  8323  		jx9MemObjRelease(&sKey);
  8324  	}
  8325  	return rc;
  8326  }
  8327  /*
  8328   * [CAPIREF: jx9_array_count()]
  8329   * Please refer to the official documentation for function purpose and expected parameters.
  8330   */
  8331  JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray)
  8332  {
  8333  	jx9_hashmap *pMap;
  8334  	/* Make sure we are dealing with a valid hashmap */
  8335  	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8336  		return 0;
  8337  	}
  8338  	/* Point to the internal representation of the hashmap */
  8339  	pMap = (jx9_hashmap *)pArray->x.pOther;
  8340  	return pMap->nEntry;
  8341  }
  8342  /*
  8343   * [CAPIREF: jx9_context_output()]
  8344   * Please refer to the official documentation for function purpose and expected parameters.
  8345   */
  8346  JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen)
  8347  {
  8348  	SyString sData;
  8349  	int rc;
  8350  	if( nLen < 0 ){
  8351  		nLen = (int)SyStrlen(zString);
  8352  	}
  8353  	SyStringInitFromBuf(&sData, zString, nLen);
  8354  	rc = jx9VmOutputConsume(pCtx->pVm, &sData);
  8355  	return rc;
  8356  }
  8357  /*
  8358   * [CAPIREF: jx9_context_throw_error()]
  8359   * Please refer to the official documentation for function purpose and expected parameters.
  8360   */
  8361  JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr)
  8362  {
  8363  	int rc = JX9_OK;
  8364  	if( zErr ){
  8365  		rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr);
  8366  	}
  8367  	return rc;
  8368  }
  8369  /*
  8370   * [CAPIREF: jx9_context_throw_error_format()]
  8371   * Please refer to the official documentation for function purpose and expected parameters.
  8372   */
  8373  JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...)
  8374  {
  8375  	va_list ap;
  8376  	int rc;
  8377  	if( zFormat == 0){
  8378  		return JX9_OK;
  8379  	}
  8380  	va_start(ap, zFormat);
  8381  	rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
  8382  	va_end(ap);
  8383  	return rc;
  8384  }
  8385  /*
  8386   * [CAPIREF: jx9_context_random_num()]
  8387   * Please refer to the official documentation for function purpose and expected parameters.
  8388   */
  8389  JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx)
  8390  {
  8391  	sxu32 n;
  8392  	n = jx9VmRandomNum(pCtx->pVm);
  8393  	return n;
  8394  }
  8395  /*
  8396   * [CAPIREF: jx9_context_random_string()]
  8397   * Please refer to the official documentation for function purpose and expected parameters.
  8398   */
  8399  JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen)
  8400  {
  8401  	if( nBuflen < 3 ){
  8402  		return JX9_CORRUPT;
  8403  	}
  8404  	jx9VmRandomString(pCtx->pVm, zBuf, nBuflen);
  8405  	return JX9_OK;
  8406  }
  8407  /*
  8408   * [CAPIREF: jx9_context_user_data()]
  8409   * Please refer to the official documentation for function purpose and expected parameters.
  8410   */
  8411  JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx)
  8412  {
  8413  	return pCtx->pFunc->pUserData;
  8414  }
  8415  /*
  8416   * [CAPIREF: jx9_context_push_aux_data()]
  8417   * Please refer to the official documentation for function purpose and expected parameters.
  8418   */
  8419  JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData)
  8420  {
  8421  	jx9_aux_data sAux;
  8422  	int rc;
  8423  	sAux.pAuxData = pUserData;
  8424  	rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux);
  8425  	return rc;
  8426  }
  8427  /*
  8428   * [CAPIREF: jx9_context_peek_aux_data()]
  8429   * Please refer to the official documentation for function purpose and expected parameters.
  8430   */
  8431  JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx)
  8432  {
  8433  	jx9_aux_data *pAux;
  8434  	pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux);
  8435  	return pAux ? pAux->pAuxData : 0;
  8436  }
  8437  /*
  8438   * [CAPIREF: jx9_context_pop_aux_data()]
  8439   * Please refer to the official documentation for function purpose and expected parameters.
  8440   */
  8441  JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx)
  8442  {
  8443  	jx9_aux_data *pAux;
  8444  	pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux);
  8445  	return pAux ? pAux->pAuxData : 0;
  8446  }
  8447  /*
  8448   * [CAPIREF: jx9_context_result_buf_length()]
  8449   * Please refer to the official documentation for function purpose and expected parameters.
  8450   */
  8451  JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx)
  8452  {
  8453  	return SyBlobLength(&pCtx->pRet->sBlob);
  8454  }
  8455  /*
  8456   * [CAPIREF: jx9_function_name()]
  8457   * Please refer to the official documentation for function purpose and expected parameters.
  8458   */
  8459  JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx)
  8460  {
  8461  	SyString *pName;
  8462  	pName = &pCtx->pFunc->sName;
  8463  	return pName->zString;
  8464  }
  8465  /*
  8466   * [CAPIREF: jx9_value_int()]
  8467   * Please refer to the official documentation for function purpose and expected parameters.
  8468   */
  8469  JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue)
  8470  {
  8471  	/* Invalidate any prior representation */
  8472  	jx9MemObjRelease(pVal);
  8473  	pVal->x.iVal = (jx9_int64)iValue;
  8474  	MemObjSetType(pVal, MEMOBJ_INT);
  8475  	return JX9_OK;
  8476  }
  8477  /*
  8478   * [CAPIREF: jx9_value_int64()]
  8479   * Please refer to the official documentation for function purpose and expected parameters.
  8480   */
  8481  JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue)
  8482  {
  8483  	/* Invalidate any prior representation */
  8484  	jx9MemObjRelease(pVal);
  8485  	pVal->x.iVal = iValue;
  8486  	MemObjSetType(pVal, MEMOBJ_INT);
  8487  	return JX9_OK;
  8488  }
  8489  /*
  8490   * [CAPIREF: jx9_value_bool()]
  8491   * Please refer to the official documentation for function purpose and expected parameters.
  8492   */
  8493  JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool)
  8494  {
  8495  	/* Invalidate any prior representation */
  8496  	jx9MemObjRelease(pVal);
  8497  	pVal->x.iVal = iBool ? 1 : 0;
  8498  	MemObjSetType(pVal, MEMOBJ_BOOL);
  8499  	return JX9_OK;
  8500  }
  8501  /*
  8502   * [CAPIREF: jx9_value_null()]
  8503   * Please refer to the official documentation for function purpose and expected parameters.
  8504   */
  8505  JX9_PRIVATE int jx9_value_null(jx9_value *pVal)
  8506  {
  8507  	/* Invalidate any prior representation and set the NULL flag */
  8508  	jx9MemObjRelease(pVal);
  8509  	return JX9_OK;
  8510  }
  8511  /*
  8512   * [CAPIREF: jx9_value_double()]
  8513   * Please refer to the official documentation for function purpose and expected parameters.
  8514   */
  8515  JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value)
  8516  {
  8517  	/* Invalidate any prior representation */
  8518  	jx9MemObjRelease(pVal);
  8519  	pVal->x.rVal = (jx9_real)Value;
  8520  	MemObjSetType(pVal, MEMOBJ_REAL);
  8521  	/* Try to get an integer representation also */
  8522  	jx9MemObjTryInteger(pVal);
  8523  	return JX9_OK;
  8524  }
  8525  /*
  8526   * [CAPIREF: jx9_value_string()]
  8527   * Please refer to the official documentation for function purpose and expected parameters.
  8528   */
  8529  JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen)
  8530  {
  8531  	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
  8532  		/* Invalidate any prior representation */
  8533  		jx9MemObjRelease(pVal);
  8534  		MemObjSetType(pVal, MEMOBJ_STRING);
  8535  	}
  8536  	if( zString ){
  8537  		if( nLen < 0 ){
  8538  			/* Compute length automatically */
  8539  			nLen = (int)SyStrlen(zString);
  8540  		}
  8541  		SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen);
  8542  	}
  8543  	return JX9_OK;
  8544  }
  8545  /*
  8546   * [CAPIREF: jx9_value_string_format()]
  8547   * Please refer to the official documentation for function purpose and expected parameters.
  8548   */
  8549  JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...)
  8550  {
  8551  	va_list ap;
  8552  	int rc;
  8553  	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
  8554  		/* Invalidate any prior representation */
  8555  		jx9MemObjRelease(pVal);
  8556  		MemObjSetType(pVal, MEMOBJ_STRING);
  8557  	}
  8558  	va_start(ap, zFormat);
  8559  	rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
  8560  	va_end(ap);
  8561  	return JX9_OK;
  8562  }
  8563  /*
  8564   * [CAPIREF: jx9_value_reset_string_cursor()]
  8565   * Please refer to the official documentation for function purpose and expected parameters.
  8566   */
  8567  JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal)
  8568  {
  8569  	/* Reset the string cursor */
  8570  	SyBlobReset(&pVal->sBlob);
  8571  	return JX9_OK;
  8572  }
  8573  /*
  8574   * [CAPIREF: jx9_value_resource()]
  8575   * Please refer to the official documentation for function purpose and expected parameters.
  8576   */
  8577  JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData)
  8578  {
  8579  	/* Invalidate any prior representation */
  8580  	jx9MemObjRelease(pVal);
  8581  	/* Reflect the new type */
  8582  	pVal->x.pOther = pUserData;
  8583  	MemObjSetType(pVal, MEMOBJ_RES);
  8584  	return JX9_OK;
  8585  }
  8586  /*
  8587   * [CAPIREF: jx9_value_release()]
  8588   * Please refer to the official documentation for function purpose and expected parameters.
  8589   */
  8590  JX9_PRIVATE int jx9_value_release(jx9_value *pVal)
  8591  {
  8592  	jx9MemObjRelease(pVal);
  8593  	return JX9_OK;
  8594  }
  8595  /*
  8596   * [CAPIREF: jx9_value_is_int()]
  8597   * Please refer to the official documentation for function purpose and expected parameters.
  8598   */
  8599  JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal)
  8600  {
  8601  	return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE;
  8602  }
  8603  /*
  8604   * [CAPIREF: jx9_value_is_float()]
  8605   * Please refer to the official documentation for function purpose and expected parameters.
  8606   */
  8607  JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal)
  8608  {
  8609  	return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE;
  8610  }
  8611  /*
  8612   * [CAPIREF: jx9_value_is_bool()]
  8613   * Please refer to the official documentation for function purpose and expected parameters.
  8614   */
  8615  JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal)
  8616  {
  8617  	return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE;
  8618  }
  8619  /*
  8620   * [CAPIREF: jx9_value_is_string()]
  8621   * Please refer to the official documentation for function purpose and expected parameters.
  8622   */
  8623  JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal)
  8624  {
  8625  	return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE;
  8626  }
  8627  /*
  8628   * [CAPIREF: jx9_value_is_null()]
  8629   * Please refer to the official documentation for function purpose and expected parameters.
  8630   */
  8631  JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal)
  8632  {
  8633  	return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE;
  8634  }
  8635  /*
  8636   * [CAPIREF: jx9_value_is_numeric()]
  8637   * Please refer to the official documentation for function purpose and expected parameters.
  8638   */
  8639  JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal)
  8640  {
  8641  	int rc;
  8642  	rc = jx9MemObjIsNumeric(pVal);
  8643  	return rc;
  8644  }
  8645  /*
  8646   * [CAPIREF: jx9_value_is_callable()]
  8647   * Please refer to the official documentation for function purpose and expected parameters.
  8648   */
  8649  JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal)
  8650  {
  8651  	int rc;
  8652  	rc = jx9VmIsCallable(pVal->pVm, pVal);
  8653  	return rc;
  8654  }
  8655  /*
  8656   * [CAPIREF: jx9_value_is_scalar()]
  8657   * Please refer to the official documentation for function purpose and expected parameters.
  8658   */
  8659  JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal)
  8660  {
  8661  	return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE;
  8662  }
  8663  /*
  8664   * [CAPIREF: jx9_value_is_json_array()]
  8665   * Please refer to the official documentation for function purpose and expected parameters.
  8666   */
  8667  JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal)
  8668  {
  8669  	return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE;
  8670  }
  8671  /*
  8672   * [CAPIREF: jx9_value_is_json_object()]
  8673   * Please refer to the official documentation for function purpose and expected parameters.
  8674   */
  8675  JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal)
  8676  {
  8677  	jx9_hashmap *pMap;
  8678  	if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){
  8679  		return FALSE;
  8680  	}
  8681  	pMap = (jx9_hashmap *)pVal->x.pOther;
  8682  	if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){
  8683  		return FALSE;
  8684  	}
  8685  	return TRUE;
  8686  }
  8687  /*
  8688   * [CAPIREF: jx9_value_is_resource()]
  8689   * Please refer to the official documentation for function purpose and expected parameters.
  8690   */
  8691  JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal)
  8692  {
  8693  	return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE;
  8694  }
  8695  /*
  8696   * [CAPIREF: jx9_value_is_empty()]
  8697   * Please refer to the official documentation for function purpose and expected parameters.
  8698   */
  8699  JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal)
  8700  {
  8701  	int rc;
  8702  	rc = jx9MemObjIsEmpty(pVal);
  8703  	return rc;
  8704  }
  8705  /*
  8706   * ----------------------------------------------------------
  8707   * File: jx9_builtin.c
  8708   * MD5: 97ae6ddf8ded9fe14634060675e12f80
  8709   * ----------------------------------------------------------
  8710   */
  8711  /*
  8712   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  8713   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  8714   * Version 1.7.2
  8715   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  8716   * please contact Symisc Systems via:
  8717   *       legal@symisc.net
  8718   *       licensing@symisc.net
  8719   *       contact@symisc.net
  8720   * or visit:
  8721   *      http://jx9.symisc.net/
  8722   */
  8723   /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
  8724  #ifndef JX9_AMALGAMATION
  8725  #include "jx9Int.h"
  8726  #endif
  8727  /* This file implement built-in 'foreign' functions for the JX9 engine */
  8728  /*
  8729   * Section:
  8730   *    Variable handling Functions.
  8731   * Authors:
  8732   *    Symisc Systems, devel@symisc.net.
  8733   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
  8734   * Status:
  8735   *    Stable.
  8736   */
  8737  /*
  8738   * bool is_bool($var)
  8739   *  Finds out whether a variable is a boolean.
  8740   * Parameters
  8741   *   $var: The variable being evaluated.
  8742   * Return
  8743   *  TRUE if var is a boolean. False otherwise.
  8744   */
  8745  static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8746  {
  8747  	int res = 0; /* Assume false by default */
  8748  	if( nArg > 0 ){
  8749  		res = jx9_value_is_bool(apArg[0]);
  8750  	}
  8751  	/* Query result */
  8752  	jx9_result_bool(pCtx, res);
  8753  	return JX9_OK;
  8754  }
  8755  /*
  8756   * bool is_float($var)
  8757   * bool is_real($var)
  8758   * bool is_double($var)
  8759   *  Finds out whether a variable is a float.
  8760   * Parameters
  8761   *   $var: The variable being evaluated.
  8762   * Return
  8763   *  TRUE if var is a float. False otherwise.
  8764   */
  8765  static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8766  {
  8767  	int res = 0; /* Assume false by default */
  8768  	if( nArg > 0 ){
  8769  		res = jx9_value_is_float(apArg[0]);
  8770  	}
  8771  	/* Query result */
  8772  	jx9_result_bool(pCtx, res);
  8773  	return JX9_OK;
  8774  }
  8775  /*
  8776   * bool is_int($var)
  8777   * bool is_integer($var)
  8778   * bool is_long($var)
  8779   *  Finds out whether a variable is an integer.
  8780   * Parameters
  8781   *   $var: The variable being evaluated.
  8782   * Return
  8783   *  TRUE if var is an integer. False otherwise.
  8784   */
  8785  static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8786  {
  8787  	int res = 0; /* Assume false by default */
  8788  	if( nArg > 0 ){
  8789  		res = jx9_value_is_int(apArg[0]);
  8790  	}
  8791  	/* Query result */
  8792  	jx9_result_bool(pCtx, res);
  8793  	return JX9_OK;
  8794  }
  8795  /*
  8796   * bool is_string($var)
  8797   *  Finds out whether a variable is a string.
  8798   * Parameters
  8799   *   $var: The variable being evaluated.
  8800   * Return
  8801   *  TRUE if var is string. False otherwise.
  8802   */
  8803  static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8804  {
  8805  	int res = 0; /* Assume false by default */
  8806  	if( nArg > 0 ){
  8807  		res = jx9_value_is_string(apArg[0]);
  8808  	}
  8809  	/* Query result */
  8810  	jx9_result_bool(pCtx, res);
  8811  	return JX9_OK;
  8812  }
  8813  /*
  8814   * bool is_null($var)
  8815   *  Finds out whether a variable is NULL.
  8816   * Parameters
  8817   *   $var: The variable being evaluated.
  8818   * Return
  8819   *  TRUE if var is NULL. False otherwise.
  8820   */
  8821  static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8822  {
  8823  	int res = 0; /* Assume false by default */
  8824  	if( nArg > 0 ){
  8825  		res = jx9_value_is_null(apArg[0]);
  8826  	}
  8827  	/* Query result */
  8828  	jx9_result_bool(pCtx, res);
  8829  	return JX9_OK;
  8830  }
  8831  /*
  8832   * bool is_numeric($var)
  8833   *  Find out whether a variable is NULL.
  8834   * Parameters
  8835   *  $var: The variable being evaluated.
  8836   * Return
  8837   *  True if var is numeric. False otherwise.
  8838   */
  8839  static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8840  {
  8841  	int res = 0; /* Assume false by default */
  8842  	if( nArg > 0 ){
  8843  		res = jx9_value_is_numeric(apArg[0]);
  8844  	}
  8845  	/* Query result */
  8846  	jx9_result_bool(pCtx, res);
  8847  	return JX9_OK;
  8848  }
  8849  /*
  8850   * bool is_scalar($var)
  8851   *  Find out whether a variable is a scalar.
  8852   * Parameters
  8853   *  $var: The variable being evaluated.
  8854   * Return
  8855   *  True if var is scalar. False otherwise.
  8856   */
  8857  static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8858  {
  8859  	int res = 0; /* Assume false by default */
  8860  	if( nArg > 0 ){
  8861  		res = jx9_value_is_scalar(apArg[0]);
  8862  	}
  8863  	/* Query result */
  8864  	jx9_result_bool(pCtx, res);
  8865  	return JX9_OK;
  8866  }
  8867  /*
  8868   * bool is_array($var)
  8869   *  Find out whether a variable is an array.
  8870   * Parameters
  8871   *  $var: The variable being evaluated.
  8872   * Return
  8873   *  True if var is an array. False otherwise.
  8874   */
  8875  static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8876  {
  8877  	int res = 0; /* Assume false by default */
  8878  	if( nArg > 0 ){
  8879  		res = jx9_value_is_json_array(apArg[0]);
  8880  	}
  8881  	/* Query result */
  8882  	jx9_result_bool(pCtx, res);
  8883  	return JX9_OK;
  8884  }
  8885  /*
  8886   * bool is_object($var)
  8887   *  Find out whether a variable is an object.
  8888   * Parameters
  8889   *  $var: The variable being evaluated.
  8890   * Return
  8891   *  True if var is an object. False otherwise.
  8892   */
  8893  static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8894  {
  8895  	int res = 0; /* Assume false by default */
  8896  	if( nArg > 0 ){
  8897  		res = jx9_value_is_json_object(apArg[0]);
  8898  	}
  8899  	/* Query result */
  8900  	jx9_result_bool(pCtx, res);
  8901  	return JX9_OK;
  8902  }
  8903  /*
  8904   * bool is_resource($var)
  8905   *  Find out whether a variable is a resource.
  8906   * Parameters
  8907   *  $var: The variable being evaluated.
  8908   * Return
  8909   *  True if a resource. False otherwise.
  8910   */
  8911  static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8912  {
  8913  	int res = 0; /* Assume false by default */
  8914  	if( nArg > 0 ){
  8915  		res = jx9_value_is_resource(apArg[0]);
  8916  	}
  8917  	jx9_result_bool(pCtx, res);
  8918  	return JX9_OK;
  8919  }
  8920  /*
  8921   * float floatval($var)
  8922   *  Get float value of a variable.
  8923   * Parameter
  8924   *  $var: The variable being processed.
  8925   * Return
  8926   *  the float value of a variable.
  8927   */
  8928  static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8929  {
  8930  	if( nArg < 1 ){
  8931  		/* return 0.0 */
  8932  		jx9_result_double(pCtx, 0);
  8933  	}else{
  8934  		double dval;
  8935  		/* Perform the cast */
  8936  		dval = jx9_value_to_double(apArg[0]);
  8937  		jx9_result_double(pCtx, dval);
  8938  	}
  8939  	return JX9_OK;
  8940  }
  8941  /*
  8942   * int intval($var)
  8943   *  Get integer value of a variable.
  8944   * Parameter
  8945   *  $var: The variable being processed.
  8946   * Return
  8947   *  the int value of a variable.
  8948   */
  8949  static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8950  {
  8951  	if( nArg < 1 ){
  8952  		/* return 0 */
  8953  		jx9_result_int(pCtx, 0);
  8954  	}else{
  8955  		sxi64 iVal;
  8956  		/* Perform the cast */
  8957  		iVal = jx9_value_to_int64(apArg[0]);
  8958  		jx9_result_int64(pCtx, iVal);
  8959  	}
  8960  	return JX9_OK;
  8961  }
  8962  /*
  8963   * string strval($var)
  8964   *  Get the string representation of a variable.
  8965   * Parameter
  8966   *  $var: The variable being processed.
  8967   * Return
  8968   *  the string value of a variable.
  8969   */
  8970  static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8971  {
  8972  	if( nArg < 1 ){
  8973  		/* return NULL */
  8974  		jx9_result_null(pCtx);
  8975  	}else{
  8976  		const char *zVal;
  8977  		int iLen = 0; /* cc -O6 warning */
  8978  		/* Perform the cast */
  8979  		zVal = jx9_value_to_string(apArg[0], &iLen);
  8980  		jx9_result_string(pCtx, zVal, iLen);
  8981  	}
  8982  	return JX9_OK;
  8983  }
  8984  /*
  8985   * bool empty($var)
  8986   *  Determine whether a variable is empty.
  8987   * Parameters
  8988   *   $var: The variable being checked.
  8989   * Return
  8990   *  0 if var has a non-empty and non-zero value.1 otherwise.
  8991   */
  8992  static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8993  {
  8994  	int res = 1; /* Assume empty by default */
  8995  	if( nArg > 0 ){
  8996  		res = jx9_value_is_empty(apArg[0]);
  8997  	}
  8998  	jx9_result_bool(pCtx, res);
  8999  	return JX9_OK;
  9000  	
  9001  }
  9002  #ifndef JX9_DISABLE_BUILTIN_FUNC
  9003  #ifdef JX9_ENABLE_MATH_FUNC
  9004  /*
  9005   * Section:
  9006   *    Math Functions.
  9007   * Authors:
  9008   *    Symisc Systems, devel@symisc.net.
  9009   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
  9010   * Status:
  9011   *    Stable.
  9012   */
  9013  #include <stdlib.h> /* abs */
  9014  #include <math.h>
  9015  /*
  9016   * float sqrt(float $arg )
  9017   *  Square root of the given number.
  9018   * Parameter
  9019   *  The number to process.
  9020   * Return
  9021   *  The square root of arg or the special value Nan of failure.
  9022   */
  9023  static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9024  {
  9025  	double r, x;
  9026  	if( nArg < 1 ){
  9027  		/* Missing argument, return 0 */
  9028  		jx9_result_int(pCtx, 0);
  9029  		return JX9_OK;
  9030  	}
  9031  	x = jx9_value_to_double(apArg[0]);
  9032  	/* Perform the requested operation */
  9033  	r = sqrt(x);
  9034  	/* store the result back */
  9035  	jx9_result_double(pCtx, r);
  9036  	return JX9_OK;
  9037  }
  9038  /*
  9039   * float exp(float $arg )
  9040   *  Calculates the exponent of e.
  9041   * Parameter
  9042   *  The number to process.
  9043   * Return
  9044   *  'e' raised to the power of arg.
  9045   */
  9046  static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9047  {
  9048  	double r, x;
  9049  	if( nArg < 1 ){
  9050  		/* Missing argument, return 0 */
  9051  		jx9_result_int(pCtx, 0);
  9052  		return JX9_OK;
  9053  	}
  9054  	x = jx9_value_to_double(apArg[0]);
  9055  	/* Perform the requested operation */
  9056  	r = exp(x);
  9057  	/* store the result back */
  9058  	jx9_result_double(pCtx, r);
  9059  	return JX9_OK;
  9060  }
  9061  /*
  9062   * float floor(float $arg )
  9063   *  Round fractions down.
  9064   * Parameter
  9065   *  The number to process.
  9066   * Return
  9067   *  Returns the next lowest integer value by rounding down value if necessary.
  9068   */
  9069  static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9070  {
  9071  	double r, x;
  9072  	if( nArg < 1 ){
  9073  		/* Missing argument, return 0 */
  9074  		jx9_result_int(pCtx, 0);
  9075  		return JX9_OK;
  9076  	}
  9077  	x = jx9_value_to_double(apArg[0]);
  9078  	/* Perform the requested operation */
  9079  	r = floor(x);
  9080  	/* store the result back */
  9081  	jx9_result_double(pCtx, r);
  9082  	return JX9_OK;
  9083  }
  9084  /*
  9085   * float cos(float $arg )
  9086   *  Cosine.
  9087   * Parameter
  9088   *  The number to process.
  9089   * Return
  9090   *  The cosine of arg.
  9091   */
  9092  static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9093  {
  9094  	double r, x;
  9095  	if( nArg < 1 ){
  9096  		/* Missing argument, return 0 */
  9097  		jx9_result_int(pCtx, 0);
  9098  		return JX9_OK;
  9099  	}
  9100  	x = jx9_value_to_double(apArg[0]);
  9101  	/* Perform the requested operation */
  9102  	r = cos(x);
  9103  	/* store the result back */
  9104  	jx9_result_double(pCtx, r);
  9105  	return JX9_OK;
  9106  }
  9107  /*
  9108   * float acos(float $arg )
  9109   *  Arc cosine.
  9110   * Parameter
  9111   *  The number to process.
  9112   * Return
  9113   *  The arc cosine of arg.
  9114   */
  9115  static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9116  {
  9117  	double r, x;
  9118  	if( nArg < 1 ){
  9119  		/* Missing argument, return 0 */
  9120  		jx9_result_int(pCtx, 0);
  9121  		return JX9_OK;
  9122  	}
  9123  	x = jx9_value_to_double(apArg[0]);
  9124  	/* Perform the requested operation */
  9125  	r = acos(x);
  9126  	/* store the result back */
  9127  	jx9_result_double(pCtx, r);
  9128  	return JX9_OK;
  9129  }
  9130  /*
  9131   * float cosh(float $arg )
  9132   *  Hyperbolic cosine.
  9133   * Parameter
  9134   *  The number to process.
  9135   * Return
  9136   *  The hyperbolic cosine of arg.
  9137   */
  9138  static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9139  {
  9140  	double r, x;
  9141  	if( nArg < 1 ){
  9142  		/* Missing argument, return 0 */
  9143  		jx9_result_int(pCtx, 0);
  9144  		return JX9_OK;
  9145  	}
  9146  	x = jx9_value_to_double(apArg[0]);
  9147  	/* Perform the requested operation */
  9148  	r = cosh(x);
  9149  	/* store the result back */
  9150  	jx9_result_double(pCtx, r);
  9151  	return JX9_OK;
  9152  }
  9153  /*
  9154   * float sin(float $arg )
  9155   *  Sine.
  9156   * Parameter
  9157   *  The number to process.
  9158   * Return
  9159   *  The sine of arg.
  9160   */
  9161  static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9162  {
  9163  	double r, x;
  9164  	if( nArg < 1 ){
  9165  		/* Missing argument, return 0 */
  9166  		jx9_result_int(pCtx, 0);
  9167  		return JX9_OK;
  9168  	}
  9169  	x = jx9_value_to_double(apArg[0]);
  9170  	/* Perform the requested operation */
  9171  	r = sin(x);
  9172  	/* store the result back */
  9173  	jx9_result_double(pCtx, r);
  9174  	return JX9_OK;
  9175  }
  9176  /*
  9177   * float asin(float $arg )
  9178   *  Arc sine.
  9179   * Parameter
  9180   *  The number to process.
  9181   * Return
  9182   *  The arc sine of arg.
  9183   */
  9184  static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9185  {
  9186  	double r, x;
  9187  	if( nArg < 1 ){
  9188  		/* Missing argument, return 0 */
  9189  		jx9_result_int(pCtx, 0);
  9190  		return JX9_OK;
  9191  	}
  9192  	x = jx9_value_to_double(apArg[0]);
  9193  	/* Perform the requested operation */
  9194  	r = asin(x);
  9195  	/* store the result back */
  9196  	jx9_result_double(pCtx, r);
  9197  	return JX9_OK;
  9198  }
  9199  /*
  9200   * float sinh(float $arg )
  9201   *  Hyperbolic sine.
  9202   * Parameter
  9203   *  The number to process.
  9204   * Return
  9205   *  The hyperbolic sine of arg.
  9206   */
  9207  static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9208  {
  9209  	double r, x;
  9210  	if( nArg < 1 ){
  9211  		/* Missing argument, return 0 */
  9212  		jx9_result_int(pCtx, 0);
  9213  		return JX9_OK;
  9214  	}
  9215  	x = jx9_value_to_double(apArg[0]);
  9216  	/* Perform the requested operation */
  9217  	r = sinh(x);
  9218  	/* store the result back */
  9219  	jx9_result_double(pCtx, r);
  9220  	return JX9_OK;
  9221  }
  9222  /*
  9223   * float ceil(float $arg )
  9224   *  Round fractions up.
  9225   * Parameter
  9226   *  The number to process.
  9227   * Return
  9228   *  The next highest integer value by rounding up value if necessary.
  9229   */
  9230  static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9231  {
  9232  	double r, x;
  9233  	if( nArg < 1 ){
  9234  		/* Missing argument, return 0 */
  9235  		jx9_result_int(pCtx, 0);
  9236  		return JX9_OK;
  9237  	}
  9238  	x = jx9_value_to_double(apArg[0]);
  9239  	/* Perform the requested operation */
  9240  	r = ceil(x);
  9241  	/* store the result back */
  9242  	jx9_result_double(pCtx, r);
  9243  	return JX9_OK;
  9244  }
  9245  /*
  9246   * float tan(float $arg )
  9247   *  Tangent.
  9248   * Parameter
  9249   *  The number to process.
  9250   * Return
  9251   *  The tangent of arg.
  9252   */
  9253  static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9254  {
  9255  	double r, x;
  9256  	if( nArg < 1 ){
  9257  		/* Missing argument, return 0 */
  9258  		jx9_result_int(pCtx, 0);
  9259  		return JX9_OK;
  9260  	}
  9261  	x = jx9_value_to_double(apArg[0]);
  9262  	/* Perform the requested operation */
  9263  	r = tan(x);
  9264  	/* store the result back */
  9265  	jx9_result_double(pCtx, r);
  9266  	return JX9_OK;
  9267  }
  9268  /*
  9269   * float atan(float $arg )
  9270   *  Arc tangent.
  9271   * Parameter
  9272   *  The number to process.
  9273   * Return
  9274   *  The arc tangent of arg.
  9275   */
  9276  static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9277  {
  9278  	double r, x;
  9279  	if( nArg < 1 ){
  9280  		/* Missing argument, return 0 */
  9281  		jx9_result_int(pCtx, 0);
  9282  		return JX9_OK;
  9283  	}
  9284  	x = jx9_value_to_double(apArg[0]);
  9285  	/* Perform the requested operation */
  9286  	r = atan(x);
  9287  	/* store the result back */
  9288  	jx9_result_double(pCtx, r);
  9289  	return JX9_OK;
  9290  }
  9291  /*
  9292   * float tanh(float $arg )
  9293   *  Hyperbolic tangent.
  9294   * Parameter
  9295   *  The number to process.
  9296   * Return
  9297   *  The Hyperbolic tangent of arg.
  9298   */
  9299  static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9300  {
  9301  	double r, x;
  9302  	if( nArg < 1 ){
  9303  		/* Missing argument, return 0 */
  9304  		jx9_result_int(pCtx, 0);
  9305  		return JX9_OK;
  9306  	}
  9307  	x = jx9_value_to_double(apArg[0]);
  9308  	/* Perform the requested operation */
  9309  	r = tanh(x);
  9310  	/* store the result back */
  9311  	jx9_result_double(pCtx, r);
  9312  	return JX9_OK;
  9313  }
  9314  /*
  9315   * float atan2(float $y, float $x)
  9316   *  Arc tangent of two variable.
  9317   * Parameter
  9318   *  $y = Dividend parameter.
  9319   *  $x = Divisor parameter.
  9320   * Return
  9321   *  The arc tangent of y/x in radian.
  9322   */
  9323  static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9324  {
  9325  	double r, x, y;
  9326  	if( nArg < 2 ){
  9327  		/* Missing arguments, return 0 */
  9328  		jx9_result_int(pCtx, 0);
  9329  		return JX9_OK;
  9330  	}
  9331  	y = jx9_value_to_double(apArg[0]);
  9332  	x = jx9_value_to_double(apArg[1]);
  9333  	/* Perform the requested operation */
  9334  	r = atan2(y, x);
  9335  	/* store the result back */
  9336  	jx9_result_double(pCtx, r);
  9337  	return JX9_OK;
  9338  }
  9339  /*
  9340   * float/int64 abs(float/int64 $arg )
  9341   *  Absolute value.
  9342   * Parameter
  9343   *  The number to process.
  9344   * Return
  9345   *  The absolute value of number.
  9346   */
  9347  static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9348  {
  9349  	int is_float;	
  9350  	if( nArg < 1 ){
  9351  		/* Missing argument, return 0 */
  9352  		jx9_result_int(pCtx, 0);
  9353  		return JX9_OK;
  9354  	}
  9355  	is_float = jx9_value_is_float(apArg[0]);
  9356  	if( is_float ){
  9357  		double r, x;
  9358  		x = jx9_value_to_double(apArg[0]);
  9359  		/* Perform the requested operation */
  9360  		r = fabs(x);
  9361  		jx9_result_double(pCtx, r);
  9362  	}else{
  9363  		int r, x;
  9364  		x = jx9_value_to_int(apArg[0]);
  9365  		/* Perform the requested operation */
  9366  		r = abs(x);
  9367  		jx9_result_int(pCtx, r);
  9368  	}
  9369  	return JX9_OK;
  9370  }
  9371  /*
  9372   * float log(float $arg, [int/float $base])
  9373   *  Natural logarithm.
  9374   * Parameter
  9375   *  $arg: The number to process.
  9376   *  $base: The optional logarithmic base to use. (only base-10 is supported)
  9377   * Return
  9378   *  The logarithm of arg to base, if given, or the natural logarithm.
  9379   * Note: 
  9380   *  only Natural log and base-10 log are supported. 
  9381   */
  9382  static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9383  {
  9384  	double r, x;
  9385  	if( nArg < 1 ){
  9386  		/* Missing argument, return 0 */
  9387  		jx9_result_int(pCtx, 0);
  9388  		return JX9_OK;
  9389  	}
  9390  	x = jx9_value_to_double(apArg[0]);
  9391  	/* Perform the requested operation */
  9392  	if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
  9393  		/* Base-10 log */
  9394  		r = log10(x);
  9395  	}else{
  9396  		r = log(x);
  9397  	}
  9398  	/* store the result back */
  9399  	jx9_result_double(pCtx, r);
  9400  	return JX9_OK;
  9401  }
  9402  /*
  9403   * float log10(float $arg )
  9404   *  Base-10 logarithm.
  9405   * Parameter
  9406   *  The number to process.
  9407   * Return
  9408   *  The Base-10 logarithm of the given number.
  9409   */
  9410  static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9411  {
  9412  	double r, x;
  9413  	if( nArg < 1 ){
  9414  		/* Missing argument, return 0 */
  9415  		jx9_result_int(pCtx, 0);
  9416  		return JX9_OK;
  9417  	}
  9418  	x = jx9_value_to_double(apArg[0]);
  9419  	/* Perform the requested operation */
  9420  	r = log10(x);
  9421  	/* store the result back */
  9422  	jx9_result_double(pCtx, r);
  9423  	return JX9_OK;
  9424  }
  9425  /*
  9426   * number pow(number $base, number $exp)
  9427   *  Exponential expression.
  9428   * Parameter
  9429   *  base
  9430   *  The base to use.
  9431   * exp
  9432   *  The exponent.
  9433   * Return
  9434   *  base raised to the power of exp.
  9435   *  If the result can be represented as integer it will be returned
  9436   *  as type integer, else it will be returned as type float. 
  9437   */
  9438  static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9439  {
  9440  	double r, x, y;
  9441  	if( nArg < 1 ){
  9442  		/* Missing argument, return 0 */
  9443  		jx9_result_int(pCtx, 0);
  9444  		return JX9_OK;
  9445  	}
  9446  	x = jx9_value_to_double(apArg[0]);
  9447  	y = jx9_value_to_double(apArg[1]);
  9448  	/* Perform the requested operation */
  9449  	r = pow(x, y);
  9450  	jx9_result_double(pCtx, r);
  9451  	return JX9_OK;
  9452  }
  9453  /*
  9454   * float pi(void)
  9455   *  Returns an approximation of pi. 
  9456   * Note
  9457   *  you can use the M_PI constant which yields identical results to pi(). 
  9458   * Return
  9459   *  The value of pi as float.
  9460   */
  9461  static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9462  {
  9463  	SXUNUSED(nArg); /* cc warning */
  9464  	SXUNUSED(apArg);
  9465  	jx9_result_double(pCtx, JX9_PI);
  9466  	return JX9_OK;
  9467  }
  9468  /*
  9469   * float fmod(float $x, float $y)
  9470   *  Returns the floating point remainder (modulo) of the division of the arguments. 
  9471   * Parameters
  9472   * $x
  9473   *  The dividend
  9474   * $y
  9475   *  The divisor
  9476   * Return
  9477   *  The floating point remainder of x/y.
  9478   */
  9479  static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9480  {
  9481  	double x, y, r; 
  9482  	if( nArg < 2 ){
  9483  		/* Missing arguments */
  9484  		jx9_result_double(pCtx, 0);
  9485  		return JX9_OK;
  9486  	}
  9487  	/* Extract given arguments */
  9488  	x = jx9_value_to_double(apArg[0]);
  9489  	y = jx9_value_to_double(apArg[1]);
  9490  	/* Perform the requested operation */
  9491  	r = fmod(x, y);
  9492  	/* Processing result */
  9493  	jx9_result_double(pCtx, r);
  9494  	return JX9_OK;
  9495  }
  9496  /*
  9497   * float hypot(float $x, float $y)
  9498   *  Calculate the length of the hypotenuse of a right-angle triangle . 
  9499   * Parameters
  9500   * $x
  9501   *  Length of first side
  9502   * $y
  9503   *  Length of first side
  9504   * Return
  9505   *  Calculated length of the hypotenuse.
  9506   */
  9507  static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9508  {
  9509  	double x, y, r; 
  9510  	if( nArg < 2 ){
  9511  		/* Missing arguments */
  9512  		jx9_result_double(pCtx, 0);
  9513  		return JX9_OK;
  9514  	}
  9515  	/* Extract given arguments */
  9516  	x = jx9_value_to_double(apArg[0]);
  9517  	y = jx9_value_to_double(apArg[1]);
  9518  	/* Perform the requested operation */
  9519  	r = hypot(x, y);
  9520  	/* Processing result */
  9521  	jx9_result_double(pCtx, r);
  9522  	return JX9_OK;
  9523  }
  9524  #endif /* JX9_ENABLE_MATH_FUNC */
  9525  /*
  9526   * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
  9527   *  Exponential expression.
  9528   * Parameter
  9529   *  $val
  9530   *   The value to round.
  9531   * $precision
  9532   *   The optional number of decimal digits to round to.
  9533   * $mode
  9534   *   One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
  9535   *   (not supported).
  9536   * Return
  9537   *  The rounded value.
  9538   */
  9539  static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9540  {
  9541  	int n = 0;
  9542  	double r;
  9543  	if( nArg < 1 ){
  9544  		/* Missing argument, return 0 */
  9545  		jx9_result_int(pCtx, 0);
  9546  		return JX9_OK;
  9547  	}
  9548  	/* Extract the precision if available */
  9549  	if( nArg > 1 ){
  9550  		n = jx9_value_to_int(apArg[1]);
  9551  		if( n>30 ){
  9552  			n = 30;
  9553  		}
  9554  		if( n<0 ){
  9555  			n = 0;
  9556  		}
  9557  	}
  9558  	r = jx9_value_to_double(apArg[0]);
  9559  	/* If Y==0 and X will fit in a 64-bit int, 
  9560       * handle the rounding directly.Otherwise 
  9561  	 * use our own cutsom printf [i.e:SyBufferFormat()].
  9562       */
  9563    if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
  9564      r = (double)((jx9_int64)(r+0.5));
  9565    }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
  9566      r = -(double)((jx9_int64)((-r)+0.5));
  9567    }else{
  9568  	  char zBuf[256];
  9569  	  sxu32 nLen;
  9570  	  nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
  9571  	  /* Convert the string to real number */
  9572  	  SyStrToReal(zBuf, nLen, (void *)&r, 0);
  9573    }
  9574    /* Return thr rounded value */
  9575    jx9_result_double(pCtx, r);
  9576    return JX9_OK;
  9577  }
  9578  /*
  9579   * string dechex(int $number)
  9580   *  Decimal to hexadecimal.
  9581   * Parameters
  9582   *  $number
  9583   *   Decimal value to convert
  9584   * Return
  9585   *  Hexadecimal string representation of number
  9586   */
  9587  static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9588  {
  9589  	int iVal;
  9590  	if( nArg < 1 ){
  9591  		/* Missing arguments, return null */
  9592  		jx9_result_null(pCtx);
  9593  		return JX9_OK;
  9594  	}
  9595  	/* Extract the given number */
  9596  	iVal = jx9_value_to_int(apArg[0]);
  9597  	/* Format */
  9598  	jx9_result_string_format(pCtx, "%x", iVal);
  9599  	return JX9_OK;
  9600  }
  9601  /*
  9602   * string decoct(int $number)
  9603   *  Decimal to Octal.
  9604   * Parameters
  9605   *  $number
  9606   *   Decimal value to convert
  9607   * Return
  9608   *  Octal string representation of number
  9609   */
  9610  static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9611  {
  9612  	int iVal;
  9613  	if( nArg < 1 ){
  9614  		/* Missing arguments, return null */
  9615  		jx9_result_null(pCtx);
  9616  		return JX9_OK;
  9617  	}
  9618  	/* Extract the given number */
  9619  	iVal = jx9_value_to_int(apArg[0]);
  9620  	/* Format */
  9621  	jx9_result_string_format(pCtx, "%o", iVal);
  9622  	return JX9_OK;
  9623  }
  9624  /*
  9625   * string decbin(int $number)
  9626   *  Decimal to binary.
  9627   * Parameters
  9628   *  $number
  9629   *   Decimal value to convert
  9630   * Return
  9631   *  Binary string representation of number
  9632   */
  9633  static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9634  {
  9635  	int iVal;
  9636  	if( nArg < 1 ){
  9637  		/* Missing arguments, return null */
  9638  		jx9_result_null(pCtx);
  9639  		return JX9_OK;
  9640  	}
  9641  	/* Extract the given number */
  9642  	iVal = jx9_value_to_int(apArg[0]);
  9643  	/* Format */
  9644  	jx9_result_string_format(pCtx, "%B", iVal);
  9645  	return JX9_OK;
  9646  }
  9647  /*
  9648   * int64 hexdec(string $hex_string)
  9649   *  Hexadecimal to decimal.
  9650   * Parameters
  9651   *  $hex_string
  9652   *   The hexadecimal string to convert
  9653   * Return
  9654   *  The decimal representation of hex_string 
  9655   */
  9656  static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9657  {
  9658  	const char *zString, *zEnd;
  9659  	jx9_int64 iVal;
  9660  	int nLen;
  9661  	if( nArg < 1 ){
  9662  		/* Missing arguments, return -1 */
  9663  		jx9_result_int(pCtx, -1);
  9664  		return JX9_OK;
  9665  	}
  9666  	iVal = 0;
  9667  	if( jx9_value_is_string(apArg[0]) ){
  9668  		/* Extract the given string */
  9669  		zString = jx9_value_to_string(apArg[0], &nLen);
  9670  		/* Delimit the string */
  9671  		zEnd = &zString[nLen];
  9672  		/* Ignore non hex-stream */
  9673  		while( zString < zEnd ){
  9674  			if( (unsigned char)zString[0] >= 0xc0 ){
  9675  				/* UTF-8 stream */
  9676  				zString++;
  9677  				while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
  9678  					zString++;
  9679  				}
  9680  			}else{
  9681  				if( SyisHex(zString[0]) ){
  9682  					break;
  9683  				}
  9684  				/* Ignore */
  9685  				zString++;
  9686  			}
  9687  		}
  9688  		if( zString < zEnd ){
  9689  			/* Cast */
  9690  			SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
  9691  		}
  9692  	}else{
  9693  		/* Extract as a 64-bit integer */
  9694  		iVal = jx9_value_to_int64(apArg[0]);
  9695  	}
  9696  	/* Return the number */
  9697  	jx9_result_int64(pCtx, iVal);
  9698  	return JX9_OK;
  9699  }
  9700  /*
  9701   * int64 bindec(string $bin_string)
  9702   *  Binary to decimal.
  9703   * Parameters
  9704   *  $bin_string
  9705   *   The binary string to convert
  9706   * Return
  9707   *  Returns the decimal equivalent of the binary number represented by the binary_string argument.  
  9708   */
  9709  static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9710  {
  9711  	const char *zString;
  9712  	jx9_int64 iVal;
  9713  	int nLen;
  9714  	if( nArg < 1 ){
  9715  		/* Missing arguments, return -1 */
  9716  		jx9_result_int(pCtx, -1);
  9717  		return JX9_OK;
  9718  	}
  9719  	iVal = 0;
  9720  	if( jx9_value_is_string(apArg[0]) ){
  9721  		/* Extract the given string */
  9722  		zString = jx9_value_to_string(apArg[0], &nLen);
  9723  		if( nLen > 0 ){
  9724  			/* Perform a binary cast */
  9725  			SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
  9726  		}
  9727  	}else{
  9728  		/* Extract as a 64-bit integer */
  9729  		iVal = jx9_value_to_int64(apArg[0]);
  9730  	}
  9731  	/* Return the number */
  9732  	jx9_result_int64(pCtx, iVal);
  9733  	return JX9_OK;
  9734  }
  9735  /*
  9736   * int64 octdec(string $oct_string)
  9737   *  Octal to decimal.
  9738   * Parameters
  9739   *  $oct_string
  9740   *   The octal string to convert
  9741   * Return
  9742   *  Returns the decimal equivalent of the octal number represented by the octal_string argument.  
  9743   */
  9744  static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9745  {
  9746  	const char *zString;
  9747  	jx9_int64 iVal;
  9748  	int nLen;
  9749  	if( nArg < 1 ){
  9750  		/* Missing arguments, return -1 */
  9751  		jx9_result_int(pCtx, -1);
  9752  		return JX9_OK;
  9753  	}
  9754  	iVal = 0;
  9755  	if( jx9_value_is_string(apArg[0]) ){
  9756  		/* Extract the given string */
  9757  		zString = jx9_value_to_string(apArg[0], &nLen);
  9758  		if( nLen > 0 ){
  9759  			/* Perform the cast */
  9760  			SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
  9761  		}
  9762  	}else{
  9763  		/* Extract as a 64-bit integer */
  9764  		iVal = jx9_value_to_int64(apArg[0]);
  9765  	}
  9766  	/* Return the number */
  9767  	jx9_result_int64(pCtx, iVal);
  9768  	return JX9_OK;
  9769  }
  9770  /*
  9771   * string base_convert(string $number, int $frombase, int $tobase)
  9772   *  Convert a number between arbitrary bases.
  9773   * Parameters
  9774   * $number
  9775   *  The number to convert
  9776   * $frombase
  9777   *  The base number is in
  9778   * $tobase
  9779   *  The base to convert number to
  9780   * Return
  9781   *  Number converted to base tobase 
  9782   */
  9783  static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9784  {
  9785  	int nLen, iFbase, iTobase;
  9786  	const char *zNum;
  9787  	jx9_int64 iNum;
  9788  	if( nArg < 3 ){
  9789  		/* Return the empty string*/
  9790  		jx9_result_string(pCtx, "", 0);
  9791  		return JX9_OK;
  9792  	}
  9793  	/* Base numbers */
  9794  	iFbase  = jx9_value_to_int(apArg[1]);
  9795  	iTobase = jx9_value_to_int(apArg[2]);
  9796  	if( jx9_value_is_string(apArg[0]) ){
  9797  		/* Extract the target number */
  9798  		zNum = jx9_value_to_string(apArg[0], &nLen);
  9799  		if( nLen < 1 ){
  9800  			/* Return the empty string*/
  9801  			jx9_result_string(pCtx, "", 0);
  9802  			return JX9_OK;
  9803  		}
  9804  		/* Base conversion */
  9805  		switch(iFbase){
  9806  		case 16:
  9807  			/* Hex */
  9808  			SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  9809  			break;
  9810  		case 8:
  9811  			/* Octal */
  9812  			SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  9813  			break;
  9814  		case 2:
  9815  			/* Binary */
  9816  			SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  9817  			break;
  9818  		default:
  9819  			/* Decimal */
  9820  			SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  9821  			break;
  9822  		}
  9823  	}else{
  9824  		iNum = jx9_value_to_int64(apArg[0]);
  9825  	}
  9826  	switch(iTobase){
  9827  	case 16:
  9828  		/* Hex */
  9829  		jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
  9830  		break;
  9831  	case 8:
  9832  		/* Octal */
  9833  		jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
  9834  		break;
  9835  	case 2:
  9836  		/* Binary */
  9837  		jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
  9838  		break;
  9839  	default:
  9840  		/* Decimal */
  9841  		jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
  9842  		break;
  9843  	}
  9844  	return JX9_OK;
  9845  }
  9846  /*
  9847   * Section:
  9848   *    String handling Functions.
  9849   * Authors:
  9850   *    Symisc Systems, devel@symisc.net.
  9851   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
  9852   * Status:
  9853   *    Stable.
  9854   */
  9855  /*
  9856   * string substr(string $string, int $start[, int $length ])
  9857   *  Return part of a string.
  9858   * Parameters
  9859   *  $string
  9860   *   The input string. Must be one character or longer.
  9861   * $start
  9862   *   If start is non-negative, the returned string will start at the start'th position
  9863   *   in string, counting from zero. For instance, in the string 'abcdef', the character
  9864   *   at position 0 is 'a', the character at position 2 is 'c', and so forth.
  9865   *   If start is negative, the returned string will start at the start'th character
  9866   *   from the end of string.
  9867   *   If string is less than or equal to start characters long, FALSE will be returned.
  9868   * $length
  9869   *   If length is given and is positive, the string returned will contain at most length
  9870   *   characters beginning from start (depending on the length of string).
  9871   *   If length is given and is negative, then that many characters will be omitted from
  9872   *   the end of string (after the start position has been calculated when a start is negative).
  9873   *   If start denotes the position of this truncation or beyond, false will be returned.
  9874   *   If length is given and is 0, FALSE or NULL an empty string will be returned.
  9875   *   If length is omitted, the substring starting from start until the end of the string
  9876   *   will be returned. 
  9877   * Return
  9878   *  Returns the extracted part of string, or FALSE on failure or an empty string.
  9879   */
  9880  static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9881  {
  9882  	const char *zSource, *zOfft;
  9883  	int nOfft, nLen, nSrcLen;	
  9884  	if( nArg < 2 ){
  9885  		/* return FALSE */
  9886  		jx9_result_bool(pCtx, 0);
  9887  		return JX9_OK;
  9888  	}
  9889  	/* Extract the target string */
  9890  	zSource = jx9_value_to_string(apArg[0], &nSrcLen);
  9891  	if( nSrcLen < 1 ){
  9892  		/* Empty string, return FALSE */
  9893  		jx9_result_bool(pCtx, 0);
  9894  		return JX9_OK;
  9895  	}
  9896  	nLen = nSrcLen; /* cc warning */
  9897  	/* Extract the offset */
  9898  	nOfft = jx9_value_to_int(apArg[1]);
  9899  	if( nOfft < 0 ){
  9900  		zOfft = &zSource[nSrcLen+nOfft]; 
  9901  		if( zOfft < zSource ){
  9902  			/* Invalid offset */
  9903  			jx9_result_bool(pCtx, 0);
  9904  			return JX9_OK;
  9905  		}
  9906  		nLen = (int)(&zSource[nSrcLen]-zOfft);
  9907  		nOfft = (int)(zOfft-zSource);
  9908  	}else if( nOfft >= nSrcLen ){
  9909  		/* Invalid offset */
  9910  		jx9_result_bool(pCtx, 0);
  9911  		return JX9_OK;
  9912  	}else{
  9913  		zOfft = &zSource[nOfft];
  9914  		nLen = nSrcLen - nOfft;
  9915  	}
  9916  	if( nArg > 2 ){
  9917  		/* Extract the length */
  9918  		nLen = jx9_value_to_int(apArg[2]);
  9919  		if( nLen == 0 ){
  9920  			/* Invalid length, return an empty string */
  9921  			jx9_result_string(pCtx, "", 0);
  9922  			return JX9_OK;
  9923  		}else if( nLen < 0 ){
  9924  			nLen = nSrcLen + nLen - nOfft;
  9925  			if( nLen < 1 ){
  9926  				/* Invalid  length */
  9927  				nLen = nSrcLen - nOfft;
  9928  			}
  9929  		}
  9930  		if( nLen + nOfft > nSrcLen ){
  9931  			/* Invalid length */
  9932  			nLen = nSrcLen - nOfft;
  9933  		}
  9934  	}
  9935  	/* Return the substring */
  9936  	jx9_result_string(pCtx, zOfft, nLen);
  9937  	return JX9_OK;
  9938  }
  9939  /*
  9940   * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
  9941   *  Binary safe comparison of two strings from an offset, up to length characters.
  9942   * Parameters
  9943   *  $main_str
  9944   *  The main string being compared.
  9945   *  $str
  9946   *   The secondary string being compared.
  9947   * $offset
  9948   *  The start position for the comparison. If negative, it starts counting from
  9949   *  the end of the string.
  9950   * $length
  9951   *  The length of the comparison. The default value is the largest of the length 
  9952   *  of the str compared to the length of main_str less the offset.
  9953   * $case_insensitivity
  9954   *  If case_insensitivity is TRUE, comparison is case insensitive.
  9955   * Return
  9956   *  Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
  9957   *  str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
  9958   *  or length is set and is less than 1, substr_compare() prints a warning and returns FALSE. 
  9959   */
  9960  static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9961  {
  9962  	const char *zSource, *zOfft, *zSub;
  9963  	int nOfft, nLen, nSrcLen, nSublen;
  9964  	int iCase = 0;
  9965  	int rc;
  9966  	if( nArg < 3 ){
  9967  		/* Missing arguments, return FALSE */
  9968  		jx9_result_bool(pCtx, 0);
  9969  		return JX9_OK;
  9970  	}
  9971  	/* Extract the target string */
  9972  	zSource = jx9_value_to_string(apArg[0], &nSrcLen);
  9973  	if( nSrcLen < 1 ){
  9974  		/* Empty string, return FALSE */
  9975  		jx9_result_bool(pCtx, 0);
  9976  		return JX9_OK;
  9977  	}
  9978  	nLen = nSrcLen; /* cc warning */
  9979  	/* Extract the substring */
  9980  	zSub = jx9_value_to_string(apArg[1], &nSublen);
  9981  	if( nSublen < 1 || nSublen > nSrcLen){
  9982  		/* Empty string, return FALSE */
  9983  		jx9_result_bool(pCtx, 0);
  9984  		return JX9_OK;
  9985  	}
  9986  	/* Extract the offset */
  9987  	nOfft = jx9_value_to_int(apArg[2]);
  9988  	if( nOfft < 0 ){
  9989  		zOfft = &zSource[nSrcLen+nOfft]; 
  9990  		if( zOfft < zSource ){
  9991  			/* Invalid offset */
  9992  			jx9_result_bool(pCtx, 0);
  9993  			return JX9_OK;
  9994  		}
  9995  		nLen = (int)(&zSource[nSrcLen]-zOfft);
  9996  		nOfft = (int)(zOfft-zSource);
  9997  	}else if( nOfft >= nSrcLen ){
  9998  		/* Invalid offset */
  9999  		jx9_result_bool(pCtx, 0);
 10000  		return JX9_OK;
 10001  	}else{
 10002  		zOfft = &zSource[nOfft];
 10003  		nLen = nSrcLen - nOfft;
 10004  	}
 10005  	if( nArg > 3 ){
 10006  		/* Extract the length */
 10007  		nLen = jx9_value_to_int(apArg[3]);
 10008  		if( nLen < 1 ){
 10009  			/* Invalid  length */
 10010  			jx9_result_int(pCtx, 1);
 10011  			return JX9_OK;
 10012  		}else if( nLen + nOfft > nSrcLen ){
 10013  			/* Invalid length */
 10014  			nLen = nSrcLen - nOfft;
 10015  		}
 10016  		if( nArg > 4 ){
 10017  			/* Case-sensitive or not */
 10018  			iCase = jx9_value_to_bool(apArg[4]);
 10019  		}
 10020  	}
 10021  	/* Perform the comparison */
 10022  	if( iCase ){
 10023  		rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
 10024  	}else{
 10025  		rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
 10026  	}
 10027  	/* Comparison result */
 10028  	jx9_result_int(pCtx, rc);
 10029  	return JX9_OK;
 10030  }
 10031  /*
 10032   * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
 10033   *  Count the number of substring occurrences.
 10034   * Parameters
 10035   * $haystack
 10036   *   The string to search in
 10037   * $needle
 10038   *   The substring to search for
 10039   * $offset
 10040   *  The offset where to start counting
 10041   * $length (NOT USED)
 10042   *  The maximum length after the specified offset to search for the substring.
 10043   *  It outputs a warning if the offset plus the length is greater than the haystack length.
 10044   * Return
 10045   *  Toral number of substring occurrences.
 10046   */
 10047  static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10048  {
 10049  	const char *zText, *zPattern, *zEnd;
 10050  	int nTextlen, nPatlen;
 10051  	int iCount = 0;
 10052  	sxu32 nOfft;
 10053  	sxi32 rc;
 10054  	if( nArg < 2 ){
 10055  		/* Missing arguments */
 10056  		jx9_result_int(pCtx, 0);
 10057  		return JX9_OK;
 10058  	}
 10059  	/* Point to the haystack */
 10060  	zText = jx9_value_to_string(apArg[0], &nTextlen);
 10061  	/* Point to the neddle */
 10062  	zPattern = jx9_value_to_string(apArg[1], &nPatlen);
 10063  	if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
 10064  		/* NOOP, return zero */
 10065  		jx9_result_int(pCtx, 0);
 10066  		return JX9_OK;
 10067  	}
 10068  	if( nArg > 2 ){
 10069  		int nOfft;
 10070  		/* Extract the offset */
 10071  		nOfft = jx9_value_to_int(apArg[2]);
 10072  		if( nOfft < 0 || nOfft > nTextlen ){
 10073  			/* Invalid offset, return zero */
 10074  			jx9_result_int(pCtx, 0);
 10075  			return JX9_OK;
 10076  		}
 10077  		/* Point to the desired offset */
 10078  		zText = &zText[nOfft];
 10079  		/* Adjust length */
 10080  		nTextlen -= nOfft;
 10081  	}
 10082  	/* Point to the end of the string */
 10083  	zEnd = &zText[nTextlen];
 10084  	if( nArg > 3 ){
 10085  		int nLen;
 10086  		/* Extract the length */
 10087  		nLen = jx9_value_to_int(apArg[3]);
 10088  		if( nLen < 0 || nLen > nTextlen ){
 10089  			/* Invalid length, return 0 */
 10090  			jx9_result_int(pCtx, 0);
 10091  			return JX9_OK;
 10092  		}
 10093  		/* Adjust pointer */
 10094  		nTextlen = nLen;
 10095  		zEnd = &zText[nTextlen];
 10096  	}
 10097  	/* Perform the search */
 10098  	for(;;){
 10099  		rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
 10100  		if( rc != SXRET_OK ){
 10101  			/* Pattern not found, break immediately */
 10102  			break;
 10103  		}
 10104  		/* Increment counter and update the offset */
 10105  		iCount++;
 10106  		zText += nOfft + nPatlen;
 10107  		if( zText >= zEnd ){
 10108  			break;
 10109  		}
 10110  	}
 10111  	/* Pattern count */
 10112  	jx9_result_int(pCtx, iCount);
 10113  	return JX9_OK;
 10114  }
 10115  /*
 10116   * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
 10117   *   Split a string into smaller chunks.
 10118   * Parameters
 10119   *  $body
 10120   *   The string to be chunked.
 10121   * $chunklen
 10122   *   The chunk length.
 10123   * $end
 10124   *   The line ending sequence.
 10125   * Return
 10126   *  The chunked string or NULL on failure.
 10127   */
 10128  static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10129  {
 10130  	const char *zIn, *zEnd, *zSep = "\r\n";
 10131  	int nSepLen, nChunkLen, nLen;
 10132  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 10133  		/* Nothing to split, return null */
 10134  		jx9_result_null(pCtx);
 10135  		return JX9_OK;
 10136  	}
 10137  	/* initialize/Extract arguments */
 10138  	nSepLen = (int)sizeof("\r\n") - 1;
 10139  	nChunkLen = 76;
 10140  	zIn = jx9_value_to_string(apArg[0], &nLen);
 10141  	zEnd = &zIn[nLen];
 10142  	if( nArg > 1 ){
 10143  		/* Chunk length */
 10144  		nChunkLen = jx9_value_to_int(apArg[1]);
 10145  		if( nChunkLen < 1 ){
 10146  			/* Switch back to the default length */
 10147  			nChunkLen = 76;
 10148  		}
 10149  		if( nArg > 2 ){
 10150  			/* Separator */
 10151  			zSep = jx9_value_to_string(apArg[2], &nSepLen);
 10152  			if( nSepLen < 1 ){
 10153  				/* Switch back to the default separator */
 10154  				zSep = "\r\n";
 10155  				nSepLen = (int)sizeof("\r\n") - 1;
 10156  			}
 10157  		}
 10158  	}
 10159  	/* Perform the requested operation */
 10160  	if( nChunkLen > nLen ){
 10161  		/* Nothing to split, return the string and the separator */
 10162  		jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
 10163  		return JX9_OK;
 10164  	}
 10165  	while( zIn < zEnd ){
 10166  		if( nChunkLen > (int)(zEnd-zIn) ){
 10167  			nChunkLen = (int)(zEnd - zIn);
 10168  		}
 10169  		/* Append the chunk and the separator */
 10170  		jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
 10171  		/* Point beyond the chunk */
 10172  		zIn += nChunkLen;
 10173  	}
 10174  	return JX9_OK;
 10175  }
 10176  /*
 10177   * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
 10178   *  HTML escaping of special characters.
 10179   *  The translations performed are:
 10180   *   '&' (ampersand) ==> '&amp;'
 10181   *   '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
 10182   *   "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
 10183   *   '<' (less than) ==> '&lt;'
 10184   *   '>' (greater than) ==> '&gt;'
 10185   * Parameters
 10186   *  $string
 10187   *   The string being converted.
 10188   * $flags
 10189   *   A bitmask of one or more of the following flags, which specify how to handle quotes.
 10190   *   The default is ENT_COMPAT | ENT_HTML401.
 10191   *   ENT_COMPAT 	Will convert double-quotes and leave single-quotes alone.
 10192   *   ENT_QUOTES 	Will convert both double and single quotes.
 10193   *   ENT_NOQUOTES 	Will leave both double and single quotes unconverted.
 10194   *   ENT_IGNORE 	Silently discard invalid code unit sequences instead of returning an empty string.
 10195   * $charset
 10196   *  Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
 10197   * Return
 10198   *  The escaped string or NULL on failure.
 10199   */
 10200  static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10201  {
 10202  	const char *zCur, *zIn, *zEnd;
 10203  	int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
 10204  	int nLen, c;
 10205  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 10206  		/* Missing/Invalid arguments, return NULL */
 10207  		jx9_result_null(pCtx);
 10208  		return JX9_OK;
 10209  	}
 10210  	/* Extract the target string */
 10211  	zIn = jx9_value_to_string(apArg[0], &nLen);
 10212  	zEnd = &zIn[nLen];
 10213  	/* Extract the flags if available */
 10214  	if( nArg > 1 ){
 10215  		iFlags = jx9_value_to_int(apArg[1]);
 10216  		if( iFlags < 0 ){
 10217  			iFlags = 0x01|0x40;
 10218  		}
 10219  	}
 10220  	/* Perform the requested operation */
 10221  	for(;;){
 10222  		if( zIn >= zEnd ){
 10223  			break;
 10224  		}
 10225  		zCur = zIn;
 10226  		while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
 10227  			zIn++;
 10228  		}
 10229  		if( zCur < zIn ){
 10230  			/* Append the raw string verbatim */
 10231  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 10232  		}
 10233  		if( zIn >= zEnd ){
 10234  			break;
 10235  		}
 10236  		c = zIn[0];
 10237  		if( c == '&' ){
 10238  			/* Expand '&amp;' */
 10239  			jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
 10240  		}else if( c == '<' ){
 10241  			/* Expand '&lt;' */
 10242  			jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
 10243  		}else if( c == '>' ){
 10244  			/* Expand '&gt;' */
 10245  			jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
 10246  		}else if( c == '\'' ){
 10247  			if( iFlags & 0x02 /*ENT_QUOTES*/ ){
 10248  				/* Expand '&#039;' */
 10249  				jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
 10250  			}else{
 10251  				/* Leave the single quote untouched */
 10252  				jx9_result_string(pCtx, "'", (int)sizeof(char));
 10253  			}
 10254  		}else if( c == '"' ){
 10255  			if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
 10256  				/* Expand '&quot;' */
 10257  				jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
 10258  			}else{
 10259  				/* Leave the double quote untouched */
 10260  				jx9_result_string(pCtx, "\"", (int)sizeof(char));
 10261  			}
 10262  		}
 10263  		/* Ignore the unsafe HTML character */
 10264  		zIn++;
 10265  	}
 10266  	return JX9_OK;
 10267  }
 10268  /*
 10269   * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
 10270   *  Unescape HTML entities.
 10271   * Parameters
 10272   *  $string
 10273   *   The string to decode
 10274   *  $quote_style
 10275   *    The quote style. One of the following constants:
 10276   *   ENT_COMPAT 	Will convert double-quotes and leave single-quotes alone (default)
 10277   *   ENT_QUOTES 	Will convert both double and single quotes
 10278   *   ENT_NOQUOTES 	Will leave both double and single quotes unconverted
 10279   * Return
 10280   *  The unescaped string or NULL on failure.
 10281   */
 10282  static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10283  {
 10284  	const char *zCur, *zIn, *zEnd;
 10285  	int iFlags = 0x01; /* ENT_COMPAT */
 10286  	int nLen, nJump;
 10287  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 10288  		/* Missing/Invalid arguments, return NULL */
 10289  		jx9_result_null(pCtx);
 10290  		return JX9_OK;
 10291  	}
 10292  	/* Extract the target string */
 10293  	zIn = jx9_value_to_string(apArg[0], &nLen);
 10294  	zEnd = &zIn[nLen];
 10295  	/* Extract the flags if available */
 10296  	if( nArg > 1 ){
 10297  		iFlags = jx9_value_to_int(apArg[1]);
 10298  		if( iFlags < 0 ){
 10299  			iFlags = 0x01;
 10300  		}
 10301  	}
 10302  	/* Perform the requested operation */
 10303  	for(;;){
 10304  		if( zIn >= zEnd ){
 10305  			break;
 10306  		}
 10307  		zCur = zIn;
 10308  		while( zIn < zEnd && zIn[0] != '&' ){
 10309  			zIn++;
 10310  		}
 10311  		if( zCur < zIn ){
 10312  			/* Append the raw string verbatim */
 10313  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 10314  		}
 10315  		nLen = (int)(zEnd-zIn);
 10316  		nJump = (int)sizeof(char);
 10317  		if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
 10318  			/* &amp; ==> '&' */
 10319  			jx9_result_string(pCtx, "&", (int)sizeof(char));
 10320  			nJump = (int)sizeof("&amp;")-1;
 10321  		}else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
 10322  			/* &lt; ==> < */
 10323  			jx9_result_string(pCtx, "<", (int)sizeof(char));
 10324  			nJump = (int)sizeof("&lt;")-1; 
 10325  		}else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
 10326  			/* &gt; ==> '>' */
 10327  			jx9_result_string(pCtx, ">", (int)sizeof(char));
 10328  			nJump = (int)sizeof("&gt;")-1; 
 10329  		}else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
 10330  			/* &quot; ==> '"' */
 10331  			if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
 10332  				jx9_result_string(pCtx, "\"", (int)sizeof(char));
 10333  			}else{
 10334  				/* Leave untouched */
 10335  				jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
 10336  			}
 10337  			nJump = (int)sizeof("&quot;")-1;
 10338  		}else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
 10339  			/* &#039; ==> ''' */
 10340  			if( iFlags & 0x02 /*ENT_QUOTES*/ ){
 10341  				/* Expand ''' */
 10342  				jx9_result_string(pCtx, "'", (int)sizeof(char));
 10343  			}else{
 10344  				/* Leave untouched */
 10345  				jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
 10346  			}
 10347  			nJump = (int)sizeof("&#039;")-1;
 10348  		}else if( nLen >= (int)sizeof(char) ){
 10349  			/* expand '&' */
 10350  			jx9_result_string(pCtx, "&", (int)sizeof(char));
 10351  		}else{
 10352  			/* No more input to process */
 10353  			break;
 10354  		}
 10355  		zIn += nJump;
 10356  	}
 10357  	return JX9_OK;
 10358  }
 10359  /* HTML encoding/Decoding table 
 10360   * Source: Symisc RunTime API.[chm@symisc.net]
 10361   */
 10362  static const char *azHtmlEscape[] = {
 10363   	"&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'", 
 10364  	"&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(", 
 10365  	"&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+", 
 10366  	"&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", "," 
 10367   };
 10368  /*
 10369   * array get_html_translation_table(void)
 10370   *  Returns the translation table used by htmlspecialchars() and htmlentities().
 10371   * Parameters
 10372   *  None
 10373   * Return
 10374   *  The translation table as an array or NULL on failure.
 10375   */
 10376  static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10377  {
 10378  	jx9_value *pArray, *pValue;
 10379  	sxu32 n;
 10380  	/* Element value */
 10381  	pValue = jx9_context_new_scalar(pCtx);
 10382  	if( pValue == 0 ){
 10383  		SXUNUSED(nArg); /* cc warning */
 10384  		SXUNUSED(apArg);
 10385  		/* Return NULL */
 10386  		jx9_result_null(pCtx);
 10387  		return JX9_OK;
 10388  	}
 10389  	/* Create a new array */
 10390  	pArray = jx9_context_new_array(pCtx);
 10391  	if( pArray == 0 ){
 10392  		/* Return NULL */
 10393  		jx9_result_null(pCtx);
 10394  		return JX9_OK;
 10395  	}
 10396  	/* Make the table */
 10397  	for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
 10398  		/* Prepare the value */
 10399  		jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
 10400  		/* Insert the value */
 10401  		jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
 10402  		/* Reset the string cursor */
 10403  		jx9_value_reset_string_cursor(pValue);
 10404  	}
 10405  	/* 
 10406  	 * Return the array.
 10407  	 * Don't worry about freeing memory, everything will be automatically
 10408  	 * released upon we return from this function.
 10409  	 */
 10410  	jx9_result_value(pCtx, pArray);
 10411  	return JX9_OK;
 10412  }
 10413  /*
 10414   * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
 10415   *   Convert all applicable characters to HTML entities
 10416   * Parameters
 10417   * $string
 10418   *   The input string.
 10419   * $flags
 10420   *  A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
 10421   * Return
 10422   * The encoded string.
 10423   */
 10424  static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10425  {
 10426  	int iFlags = 0x01; /* ENT_COMPAT */
 10427  	const char *zIn, *zEnd;
 10428  	int nLen, c;
 10429  	sxu32 n;
 10430  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 10431  		/* Missing/Invalid arguments, return NULL */
 10432  		jx9_result_null(pCtx);
 10433  		return JX9_OK;
 10434  	}
 10435  	/* Extract the target string */
 10436  	zIn = jx9_value_to_string(apArg[0], &nLen);
 10437  	zEnd = &zIn[nLen];
 10438  	/* Extract the flags if available */
 10439  	if( nArg > 1 ){
 10440  		iFlags = jx9_value_to_int(apArg[1]);
 10441  		if( iFlags < 0 ){
 10442  			iFlags = 0x01;
 10443  		}
 10444  	}
 10445  	/* Perform the requested operation */
 10446  	for(;;){
 10447  		if( zIn >= zEnd ){
 10448  			/* No more input to process */
 10449  			break;
 10450  		}
 10451  		c = zIn[0];
 10452  		/* Perform a linear lookup on the decoding table */
 10453  		for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
 10454  			if( azHtmlEscape[n+1][0] == c ){
 10455  				/* Got one */
 10456  				break;
 10457  			}
 10458  		}
 10459  		if( n < SX_ARRAYSIZE(azHtmlEscape) ){
 10460  			/* Output the safe sequence [i.e: '<' ==> '&lt;"] */
 10461  			if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
 10462  				/* Expand the double quote verbatim */
 10463  				jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 10464  			}else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
 10465  				/* expand single quote verbatim */
 10466  				jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 10467  			}else{
 10468  				jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
 10469  			}
 10470  		}else{
 10471  			/* Output character verbatim */
 10472  			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 10473  		}
 10474  		zIn++;
 10475  	}
 10476  	return JX9_OK;
 10477  }
 10478  /*
 10479   * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
 10480   *   Perform the reverse operation of html_entity_decode().
 10481   * Parameters
 10482   * $string
 10483   *   The input string.
 10484   * $flags
 10485   *  A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
 10486   * Return
 10487   * The decoded string.
 10488   */
 10489  static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10490  {
 10491  	const char *zCur, *zIn, *zEnd;
 10492  	int iFlags = 0x01; /* ENT_COMPAT  */
 10493  	int nLen;
 10494  	sxu32 n;
 10495  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 10496  		/* Missing/Invalid arguments, return NULL */
 10497  		jx9_result_null(pCtx);
 10498  		return JX9_OK;
 10499  	}
 10500  	/* Extract the target string */
 10501  	zIn = jx9_value_to_string(apArg[0], &nLen);
 10502  	zEnd = &zIn[nLen];
 10503  	/* Extract the flags if available */
 10504  	if( nArg > 1 ){
 10505  		iFlags = jx9_value_to_int(apArg[1]);
 10506  		if( iFlags < 0 ){
 10507  			iFlags = 0x01;
 10508  		}
 10509  	}
 10510  	/* Perform the requested operation */
 10511  	for(;;){
 10512  		if( zIn >= zEnd ){
 10513  			/* No more input to process */
 10514  			break;
 10515  		}
 10516  		zCur = zIn;
 10517  		while( zIn < zEnd && zIn[0] != '&' ){
 10518  			zIn++;
 10519  		}
 10520  		if( zCur < zIn ){
 10521  			/* Append raw string verbatim */
 10522  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 10523  		}
 10524  		if( zIn >= zEnd ){
 10525  			break;
 10526  		}
 10527  		nLen = (int)(zEnd-zIn);
 10528  		/* Find an encoded sequence */
 10529  		for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
 10530  			int iLen = (int)SyStrlen(azHtmlEscape[n]);
 10531  			if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
 10532  				/* Got one */
 10533  				zIn += iLen;
 10534  				break;
 10535  			}
 10536  		}
 10537  		if( n < SX_ARRAYSIZE(azHtmlEscape) ){
 10538  			int c = azHtmlEscape[n+1][0];
 10539  			/* Output the decoded character */
 10540  			if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/)  ){
 10541  				/* Do not process single quotes */
 10542  				jx9_result_string(pCtx, azHtmlEscape[n], -1);
 10543  			}else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
 10544  				/* Do not process double quotes */
 10545  				jx9_result_string(pCtx, azHtmlEscape[n], -1);
 10546  			}else{
 10547  				jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
 10548  			}
 10549  		}else{
 10550  			/* Append '&' */
 10551  			jx9_result_string(pCtx, "&", (int)sizeof(char));
 10552  			zIn++;
 10553  		}
 10554  	}
 10555  	return JX9_OK;
 10556  }
 10557  /*
 10558   * int strlen($string)
 10559   *  return the length of the given string.
 10560   * Parameter
 10561   *  string: The string being measured for length.
 10562   * Return
 10563   *  length of the given string.
 10564   */
 10565  static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10566  {
 10567  	int iLen = 0;
 10568  	if( nArg > 0 ){
 10569  		jx9_value_to_string(apArg[0], &iLen);
 10570  	}
 10571  	/* String length */
 10572  	jx9_result_int(pCtx, iLen);
 10573  	return JX9_OK;
 10574  }
 10575  /*
 10576   * int strcmp(string $str1, string $str2)
 10577   *  Perform a binary safe string comparison.
 10578   * Parameter
 10579   *  str1: The first string
 10580   *  str2: The second string
 10581   * Return
 10582   *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
 10583   *  than str2, and 0 if they are equal.
 10584   */
 10585  static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10586  {
 10587  	const char *z1, *z2;
 10588  	int n1, n2;
 10589  	int res;
 10590  	if( nArg < 2 ){
 10591  		res = nArg == 0 ? 0 : 1;
 10592  		jx9_result_int(pCtx, res);
 10593  		return JX9_OK;
 10594  	}
 10595  	/* Perform the comparison */
 10596  	z1 = jx9_value_to_string(apArg[0], &n1);
 10597  	z2 = jx9_value_to_string(apArg[1], &n2);
 10598  	res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
 10599  	/* Comparison result */
 10600  	jx9_result_int(pCtx, res);
 10601  	return JX9_OK;
 10602  }
 10603  /*
 10604   * int strncmp(string $str1, string $str2, int n)
 10605   *  Perform a binary safe string comparison of the first n characters.
 10606   * Parameter
 10607   *  str1: The first string
 10608   *  str2: The second string
 10609   * Return
 10610   *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
 10611   *  than str2, and 0 if they are equal.
 10612   */
 10613  static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10614  {
 10615  	const char *z1, *z2;
 10616  	int res;
 10617  	int n;
 10618  	if( nArg < 3 ){
 10619  		/* Perform a standard comparison */
 10620  		return jx9Builtin_strcmp(pCtx, nArg, apArg);
 10621  	}
 10622  	/* Desired comparison length */
 10623  	n  = jx9_value_to_int(apArg[2]);
 10624  	if( n < 0 ){
 10625  		/* Invalid length */
 10626  		jx9_result_int(pCtx, -1);
 10627  		return JX9_OK;
 10628  	}
 10629  	/* Perform the comparison */
 10630  	z1 = jx9_value_to_string(apArg[0], 0);
 10631  	z2 = jx9_value_to_string(apArg[1], 0);
 10632  	res = SyStrncmp(z1, z2, (sxu32)n);
 10633  	/* Comparison result */
 10634  	jx9_result_int(pCtx, res);
 10635  	return JX9_OK;
 10636  }
 10637  /*
 10638   * int strcasecmp(string $str1, string $str2, int n)
 10639   *  Perform a binary safe case-insensitive string comparison.
 10640   * Parameter
 10641   *  str1: The first string
 10642   *  str2: The second string
 10643   * Return
 10644   *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
 10645   *  than str2, and 0 if they are equal.
 10646   */
 10647  static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10648  {
 10649  	const char *z1, *z2;
 10650  	int n1, n2;
 10651  	int res;
 10652  	if( nArg < 2 ){
 10653  		res = nArg == 0 ? 0 : 1;
 10654  		jx9_result_int(pCtx, res);
 10655  		return JX9_OK;
 10656  	}
 10657  	/* Perform the comparison */
 10658  	z1 = jx9_value_to_string(apArg[0], &n1);
 10659  	z2 = jx9_value_to_string(apArg[1], &n2);
 10660  	res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
 10661  	/* Comparison result */
 10662  	jx9_result_int(pCtx, res);
 10663  	return JX9_OK;
 10664  }
 10665  /*
 10666   * int strncasecmp(string $str1, string $str2, int n)
 10667   *  Perform a binary safe case-insensitive string comparison of the first n characters.
 10668   * Parameter
 10669   *  $str1: The first string
 10670   *  $str2: The second string
 10671   *  $len:  The length of strings to be used in the comparison.
 10672   * Return
 10673   *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
 10674   *  than str2, and 0 if they are equal.
 10675   */
 10676  static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10677  {
 10678  	const char *z1, *z2;
 10679  	int res;
 10680  	int n;
 10681  	if( nArg < 3 ){
 10682  		/* Perform a standard comparison */
 10683  		return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
 10684  	}
 10685  	/* Desired comparison length */
 10686  	n  = jx9_value_to_int(apArg[2]);
 10687  	if( n < 0 ){
 10688  		/* Invalid length */
 10689  		jx9_result_int(pCtx, -1);
 10690  		return JX9_OK;
 10691  	}
 10692  	/* Perform the comparison */
 10693  	z1 = jx9_value_to_string(apArg[0], 0);
 10694  	z2 = jx9_value_to_string(apArg[1], 0);
 10695  	res = SyStrnicmp(z1, z2, (sxu32)n);
 10696  	/* Comparison result */
 10697  	jx9_result_int(pCtx, res);
 10698  	return JX9_OK;
 10699  }
 10700  /*
 10701   * Implode context [i.e: it's private data].
 10702   * A pointer to the following structure is forwarded
 10703   * verbatim to the array walker callback defined below.
 10704   */
 10705  struct implode_data {
 10706  	jx9_context *pCtx;    /* Call context */
 10707  	int bRecursive;       /* TRUE if recursive implode [this is a symisc eXtension] */
 10708  	const char *zSep;     /* Arguments separator if any */
 10709  	int nSeplen;          /* Separator length */
 10710  	int bFirst;           /* TRUE if first call */
 10711  	int nRecCount;        /* Recursion count to avoid infinite loop */
 10712  };
 10713  /*
 10714   * Implode walker callback for the [jx9_array_walk()] interface.
 10715   * The following routine is invoked for each array entry passed
 10716   * to the implode() function.
 10717   */
 10718  static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
 10719  {
 10720  	struct implode_data *pData = (struct implode_data *)pUserData;
 10721  	const char *zData;
 10722  	int nLen;
 10723  	if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
 10724  		if( pData->nSeplen > 0 ){
 10725  			if( !pData->bFirst ){
 10726  				/* append the separator first */
 10727  				jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
 10728  			}else{
 10729  				pData->bFirst = 0;
 10730  			}
 10731  		}
 10732  		/* Recurse */
 10733  		pData->bFirst = 1;
 10734  		pData->nRecCount++;
 10735  		jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
 10736  		pData->nRecCount--;
 10737  		return JX9_OK;
 10738  	}
 10739  	/* Extract the string representation of the entry value */
 10740  	zData = jx9_value_to_string(pValue, &nLen);
 10741  	if( nLen > 0 ){
 10742  		if( pData->nSeplen > 0 ){
 10743  			if( !pData->bFirst ){
 10744  				/* append the separator first */
 10745  				jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
 10746  			}else{
 10747  				pData->bFirst = 0;
 10748  			}
 10749  		}
 10750  		jx9_result_string(pData->pCtx, zData, nLen);
 10751  	}else{
 10752  		SXUNUSED(pKey); /* cc warning */
 10753  	}
 10754  	return JX9_OK;
 10755  }
 10756  /*
 10757   * string implode(string $glue, array $pieces, ...)
 10758   * string implode(array $pieces, ...)
 10759   *  Join array elements with a string.
 10760   * $glue
 10761   *   Defaults to an empty string. This is not the preferred usage of implode() as glue
 10762   *   would be the second parameter and thus, the bad prototype would be used.
 10763   * $pieces
 10764   *   The array of strings to implode.
 10765   * Return
 10766   *  Returns a string containing a string representation of all the array elements in the same
 10767   *  order, with the glue string between each element. 
 10768   */
 10769  static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10770  {
 10771  	struct implode_data imp_data;
 10772  	int i = 1;
 10773  	if( nArg < 1 ){
 10774  		/* Missing argument, return NULL */
 10775  		jx9_result_null(pCtx);
 10776  		return JX9_OK;
 10777  	}
 10778  	/* Prepare the implode context */
 10779  	imp_data.pCtx = pCtx;
 10780  	imp_data.bRecursive = 0;
 10781  	imp_data.bFirst = 1;
 10782  	imp_data.nRecCount = 0;
 10783  	if( !jx9_value_is_json_array(apArg[0]) ){
 10784  		imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
 10785  	}else{
 10786  		imp_data.zSep = 0;
 10787  		imp_data.nSeplen = 0;
 10788  		i = 0;
 10789  	}
 10790  	jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
 10791  	/* Start the 'join' process */
 10792  	while( i < nArg ){
 10793  		if( jx9_value_is_json_array(apArg[i]) ){
 10794  			/* Iterate throw array entries */
 10795  			jx9_array_walk(apArg[i], implode_callback, &imp_data);
 10796  		}else{
 10797  			const char *zData;
 10798  			int nLen;
 10799  			/* Extract the string representation of the jx9 value */
 10800  			zData = jx9_value_to_string(apArg[i], &nLen);
 10801  			if( nLen > 0 ){
 10802  				if( imp_data.nSeplen > 0 ){
 10803  					if( !imp_data.bFirst ){
 10804  						/* append the separator first */
 10805  						jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
 10806  					}else{
 10807  						imp_data.bFirst = 0;
 10808  					}
 10809  				}
 10810  				jx9_result_string(pCtx, zData, nLen);
 10811  			}
 10812  		}
 10813  		i++;
 10814  	}
 10815  	return JX9_OK;
 10816  }
 10817  /*
 10818   * string implode_recursive(string $glue, array $pieces, ...)
 10819   * Purpose
 10820   *  Same as implode() but recurse on arrays.
 10821   * Example:
 10822   *   $a = array('usr', array('home', 'dean'));
 10823   *   print implode_recursive("/", $a);
 10824   *   Will output
 10825   *     usr/home/dean.
 10826   *   While the standard implode would produce.
 10827   *    usr/Array.
 10828   * Parameter
 10829   *  Refer to implode().
 10830   * Return
 10831   *  Refer to implode().
 10832   */
 10833  static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10834  {
 10835  	struct implode_data imp_data;
 10836  	int i = 1;
 10837  	if( nArg < 1 ){
 10838  		/* Missing argument, return NULL */
 10839  		jx9_result_null(pCtx);
 10840  		return JX9_OK;
 10841  	}
 10842  	/* Prepare the implode context */
 10843  	imp_data.pCtx = pCtx;
 10844  	imp_data.bRecursive = 1;
 10845  	imp_data.bFirst = 1;
 10846  	imp_data.nRecCount = 0;
 10847  	if( !jx9_value_is_json_array(apArg[0]) ){
 10848  		imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
 10849  	}else{
 10850  		imp_data.zSep = 0;
 10851  		imp_data.nSeplen = 0;
 10852  		i = 0;
 10853  	}
 10854  	jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
 10855  	/* Start the 'join' process */
 10856  	while( i < nArg ){
 10857  		if( jx9_value_is_json_array(apArg[i]) ){
 10858  			/* Iterate throw array entries */
 10859  			jx9_array_walk(apArg[i], implode_callback, &imp_data);
 10860  		}else{
 10861  			const char *zData;
 10862  			int nLen;
 10863  			/* Extract the string representation of the jx9 value */
 10864  			zData = jx9_value_to_string(apArg[i], &nLen);
 10865  			if( nLen > 0 ){
 10866  				if( imp_data.nSeplen > 0 ){
 10867  					if( !imp_data.bFirst ){
 10868  						/* append the separator first */
 10869  						jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
 10870  					}else{
 10871  						imp_data.bFirst = 0;
 10872  					}
 10873  				}
 10874  				jx9_result_string(pCtx, zData, nLen);
 10875  			}
 10876  		}
 10877  		i++;
 10878  	}
 10879  	return JX9_OK;
 10880  }
 10881  /*
 10882   * array explode(string $delimiter, string $string[, int $limit ])
 10883   *  Returns an array of strings, each of which is a substring of string 
 10884   *  formed by splitting it on boundaries formed by the string delimiter. 
 10885   * Parameters
 10886   *  $delimiter
 10887   *   The boundary string.
 10888   * $string
 10889   *   The input string.
 10890   * $limit
 10891   *   If limit is set and positive, the returned array will contain a maximum
 10892   *   of limit elements with the last element containing the rest of string.
 10893   *   If the limit parameter is negative, all fields except the last -limit are returned.
 10894   *   If the limit parameter is zero, then this is treated as 1.
 10895   * Returns
 10896   *  Returns an array of strings created by splitting the string parameter
 10897   *  on boundaries formed by the delimiter.
 10898   *  If delimiter is an empty string (""), explode() will return FALSE. 
 10899   *  If delimiter contains a value that is not contained in string and a negative
 10900   *  limit is used, then an empty array will be returned, otherwise an array containing string
 10901   *  will be returned. 
 10902   * NOTE:
 10903   *  Negative limit is not supported.
 10904   */
 10905  static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 10906  {
 10907  	const char *zDelim, *zString, *zCur, *zEnd;
 10908  	int nDelim, nStrlen, iLimit;
 10909  	jx9_value *pArray;
 10910  	jx9_value *pValue;
 10911  	sxu32 nOfft;
 10912  	sxi32 rc;
 10913  	if( nArg < 2 ){
 10914  		/* Missing arguments, return FALSE */
 10915  		jx9_result_bool(pCtx, 0);
 10916  		return JX9_OK;
 10917  	}
 10918  	/* Extract the delimiter */
 10919  	zDelim = jx9_value_to_string(apArg[0], &nDelim);
 10920  	if( nDelim < 1 ){
 10921  		/* Empty delimiter, return FALSE */
 10922  		jx9_result_bool(pCtx, 0);
 10923  		return JX9_OK;
 10924  	}
 10925  	/* Extract the string */
 10926  	zString = jx9_value_to_string(apArg[1], &nStrlen);
 10927  	if( nStrlen < 1 ){
 10928  		/* Empty delimiter, return FALSE */
 10929  		jx9_result_bool(pCtx, 0);
 10930  		return JX9_OK;
 10931  	}
 10932  	/* Point to the end of the string */
 10933  	zEnd = &zString[nStrlen];
 10934  	/* Create the array */
 10935  	pArray =  jx9_context_new_array(pCtx);
 10936  	pValue = jx9_context_new_scalar(pCtx);
 10937  	if( pArray == 0 || pValue == 0 ){
 10938  		/* Out of memory, return FALSE */
 10939  		jx9_result_bool(pCtx, 0);
 10940  		return JX9_OK;
 10941  	}
 10942  	/* Set a defualt limit */
 10943  	iLimit = SXI32_HIGH;
 10944  	if( nArg > 2 ){
 10945  		iLimit = jx9_value_to_int(apArg[2]);
 10946  		 if( iLimit < 0 ){
 10947  			iLimit = -iLimit;
 10948  		}
 10949  		if( iLimit == 0 ){
 10950  			iLimit = 1;
 10951  		}
 10952  		iLimit--;
 10953  	}
 10954  	/* Start exploding */
 10955  	for(;;){
 10956  		if( zString >= zEnd ){
 10957  			/* No more entry to process */
 10958  			break;
 10959  		}
 10960  		rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
 10961  		if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
 10962  			/* Limit reached, insert the rest of the string and break */
 10963  			if( zEnd > zString ){
 10964  				jx9_value_string(pValue, zString, (int)(zEnd-zString));
 10965  				jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
 10966  			}
 10967  			break;
 10968  		}
 10969  		/* Point to the desired offset */
 10970  		zCur = &zString[nOfft];
 10971  		if( zCur > zString ){
 10972  			/* Perform the store operation */
 10973  			jx9_value_string(pValue, zString, (int)(zCur-zString));
 10974  			jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
 10975  		}
 10976  		/* Point beyond the delimiter */
 10977  		zString = &zCur[nDelim];
 10978  		/* Reset the cursor */
 10979  		jx9_value_reset_string_cursor(pValue);
 10980  	}
 10981  	/* Return the freshly created array */
 10982  	jx9_result_value(pCtx, pArray);
 10983  	/* NOTE that every allocated jx9_value will be automatically 
 10984  	 * released as soon we return from this foregin function.
 10985  	 */
 10986  	return JX9_OK;
 10987  }
 10988  /*
 10989   * string trim(string $str[, string $charlist ])
 10990   *  Strip whitespace (or other characters) from the beginning and end of a string.
 10991   * Parameters
 10992   *  $str
 10993   *   The string that will be trimmed.
 10994   * $charlist
 10995   *   Optionally, the stripped characters can also be specified using the charlist parameter.
 10996   *   Simply list all characters that you want to be stripped.
 10997   *   With .. you can specify a range of characters.
 10998   * Returns.
 10999   *  Thr processed string.
 11000   */
 11001  static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11002  {
 11003  	const char *zString;
 11004  	int nLen;
 11005  	if( nArg < 1 ){
 11006  		/* Missing arguments, return null */
 11007  		jx9_result_null(pCtx);
 11008  		return JX9_OK;
 11009  	}
 11010  	/* Extract the target string */
 11011  	zString = jx9_value_to_string(apArg[0], &nLen);
 11012  	if( nLen < 1 ){
 11013  		/* Empty string, return */
 11014  		jx9_result_string(pCtx, "", 0);
 11015  		return JX9_OK;
 11016  	}
 11017  	/* Start the trim process */
 11018  	if( nArg < 2 ){
 11019  		SyString sStr;
 11020  		/* Remove white spaces and NUL bytes */
 11021  		SyStringInitFromBuf(&sStr, zString, nLen);
 11022  		SyStringFullTrimSafe(&sStr);
 11023  		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
 11024  	}else{
 11025  		/* Char list */
 11026  		const char *zList;
 11027  		int nListlen;
 11028  		zList = jx9_value_to_string(apArg[1], &nListlen);
 11029  		if( nListlen < 1 ){
 11030  			/* Return the string unchanged */
 11031  			jx9_result_string(pCtx, zString, nLen);
 11032  		}else{
 11033  			const char *zEnd = &zString[nLen];
 11034  			const char *zCur = zString;
 11035  			const char *zPtr;
 11036  			int i;
 11037  			/* Left trim */
 11038  			for(;;){
 11039  				if( zCur >= zEnd ){
 11040  					break;
 11041  				}
 11042  				zPtr = zCur;
 11043  				for( i = 0 ; i < nListlen ; i++ ){
 11044  					if( zCur < zEnd && zCur[0] == zList[i] ){
 11045  						zCur++;
 11046  					}
 11047  				}
 11048  				if( zCur == zPtr ){
 11049  					/* No match, break immediately */
 11050  					break;
 11051  				}
 11052  			}
 11053  			/* Right trim */
 11054  			zEnd--;
 11055  			for(;;){
 11056  				if( zEnd <= zCur ){
 11057  					break;
 11058  				}
 11059  				zPtr = zEnd;
 11060  				for( i = 0 ; i < nListlen ; i++ ){
 11061  					if( zEnd > zCur && zEnd[0] == zList[i] ){
 11062  						zEnd--;
 11063  					}
 11064  				}
 11065  				if( zEnd == zPtr ){
 11066  					break;
 11067  				}
 11068  			}
 11069  			if( zCur >= zEnd ){
 11070  				/* Return the empty string */
 11071  				jx9_result_string(pCtx, "", 0);
 11072  			}else{
 11073  				zEnd++;
 11074  				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
 11075  			}
 11076  		}
 11077  	}
 11078  	return JX9_OK;
 11079  }
 11080  /*
 11081   * string rtrim(string $str[, string $charlist ])
 11082   *  Strip whitespace (or other characters) from the end of a string.
 11083   * Parameters
 11084   *  $str
 11085   *   The string that will be trimmed.
 11086   * $charlist
 11087   *   Optionally, the stripped characters can also be specified using the charlist parameter.
 11088   *   Simply list all characters that you want to be stripped.
 11089   *   With .. you can specify a range of characters.
 11090   * Returns.
 11091   *  Thr processed string.
 11092   */
 11093  static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11094  {
 11095  	const char *zString;
 11096  	int nLen;
 11097  	if( nArg < 1 ){
 11098  		/* Missing arguments, return null */
 11099  		jx9_result_null(pCtx);
 11100  		return JX9_OK;
 11101  	}
 11102  	/* Extract the target string */
 11103  	zString = jx9_value_to_string(apArg[0], &nLen);
 11104  	if( nLen < 1 ){
 11105  		/* Empty string, return */
 11106  		jx9_result_string(pCtx, "", 0);
 11107  		return JX9_OK;
 11108  	}
 11109  	/* Start the trim process */
 11110  	if( nArg < 2 ){
 11111  		SyString sStr;
 11112  		/* Remove white spaces and NUL bytes*/
 11113  		SyStringInitFromBuf(&sStr, zString, nLen);
 11114  		SyStringRightTrimSafe(&sStr);
 11115  		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
 11116  	}else{
 11117  		/* Char list */
 11118  		const char *zList;
 11119  		int nListlen;
 11120  		zList = jx9_value_to_string(apArg[1], &nListlen);
 11121  		if( nListlen < 1 ){
 11122  			/* Return the string unchanged */
 11123  			jx9_result_string(pCtx, zString, nLen);
 11124  		}else{
 11125  			const char *zEnd = &zString[nLen - 1];
 11126  			const char *zCur = zString;
 11127  			const char *zPtr;
 11128  			int i;
 11129  			/* Right trim */
 11130  			for(;;){
 11131  				if( zEnd <= zCur ){
 11132  					break;
 11133  				}
 11134  				zPtr = zEnd;
 11135  				for( i = 0 ; i < nListlen ; i++ ){
 11136  					if( zEnd > zCur && zEnd[0] == zList[i] ){
 11137  						zEnd--;
 11138  					}
 11139  				}
 11140  				if( zEnd == zPtr ){
 11141  					break;
 11142  				}
 11143  			}
 11144  			if( zEnd <= zCur ){
 11145  				/* Return the empty string */
 11146  				jx9_result_string(pCtx, "", 0);
 11147  			}else{
 11148  				zEnd++;
 11149  				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
 11150  			}
 11151  		}
 11152  	}
 11153  	return JX9_OK;
 11154  }
 11155  /*
 11156   * string ltrim(string $str[, string $charlist ])
 11157   *  Strip whitespace (or other characters) from the beginning and end of a string.
 11158   * Parameters
 11159   *  $str
 11160   *   The string that will be trimmed.
 11161   * $charlist
 11162   *   Optionally, the stripped characters can also be specified using the charlist parameter.
 11163   *   Simply list all characters that you want to be stripped.
 11164   *   With .. you can specify a range of characters.
 11165   * Returns.
 11166   *  The processed string.
 11167   */
 11168  static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11169  {
 11170  	const char *zString;
 11171  	int nLen;
 11172  	if( nArg < 1 ){
 11173  		/* Missing arguments, return null */
 11174  		jx9_result_null(pCtx);
 11175  		return JX9_OK;
 11176  	}
 11177  	/* Extract the target string */
 11178  	zString = jx9_value_to_string(apArg[0], &nLen);
 11179  	if( nLen < 1 ){
 11180  		/* Empty string, return */
 11181  		jx9_result_string(pCtx, "", 0);
 11182  		return JX9_OK;
 11183  	}
 11184  	/* Start the trim process */
 11185  	if( nArg < 2 ){
 11186  		SyString sStr;
 11187  		/* Remove white spaces and NUL byte */
 11188  		SyStringInitFromBuf(&sStr, zString, nLen);
 11189  		SyStringLeftTrimSafe(&sStr);
 11190  		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
 11191  	}else{
 11192  		/* Char list */
 11193  		const char *zList;
 11194  		int nListlen;
 11195  		zList = jx9_value_to_string(apArg[1], &nListlen);
 11196  		if( nListlen < 1 ){
 11197  			/* Return the string unchanged */
 11198  			jx9_result_string(pCtx, zString, nLen);
 11199  		}else{
 11200  			const char *zEnd = &zString[nLen];
 11201  			const char *zCur = zString;
 11202  			const char *zPtr;
 11203  			int i;
 11204  			/* Left trim */
 11205  			for(;;){
 11206  				if( zCur >= zEnd ){
 11207  					break;
 11208  				}
 11209  				zPtr = zCur;
 11210  				for( i = 0 ; i < nListlen ; i++ ){
 11211  					if( zCur < zEnd && zCur[0] == zList[i] ){
 11212  						zCur++;
 11213  					}
 11214  				}
 11215  				if( zCur == zPtr ){
 11216  					/* No match, break immediately */
 11217  					break;
 11218  				}
 11219  			}
 11220  			if( zCur >= zEnd ){
 11221  				/* Return the empty string */
 11222  				jx9_result_string(pCtx, "", 0);
 11223  			}else{
 11224  				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
 11225  			}
 11226  		}
 11227  	}
 11228  	return JX9_OK;
 11229  }
 11230  /*
 11231   * string strtolower(string $str)
 11232   *  Make a string lowercase.
 11233   * Parameters
 11234   *  $str
 11235   *   The input string.
 11236   * Returns.
 11237   *  The lowercased string.
 11238   */
 11239  static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11240  {
 11241  	const char *zString, *zCur, *zEnd;
 11242  	int nLen;
 11243  	if( nArg < 1 ){
 11244  		/* Missing arguments, return null */
 11245  		jx9_result_null(pCtx);
 11246  		return JX9_OK;
 11247  	}
 11248  	/* Extract the target string */
 11249  	zString = jx9_value_to_string(apArg[0], &nLen);
 11250  	if( nLen < 1 ){
 11251  		/* Empty string, return */
 11252  		jx9_result_string(pCtx, "", 0);
 11253  		return JX9_OK;
 11254  	}
 11255  	/* Perform the requested operation */
 11256  	zEnd = &zString[nLen];
 11257  	for(;;){
 11258  		if( zString >= zEnd ){
 11259  			/* No more input, break immediately */
 11260  			break;
 11261  		}
 11262  		if( (unsigned char)zString[0] >= 0xc0 ){
 11263  			/* UTF-8 stream, output verbatim */
 11264  			zCur = zString;
 11265  			zString++;
 11266  			while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
 11267  				zString++;
 11268  			}
 11269  			/* Append UTF-8 stream */
 11270  			jx9_result_string(pCtx, zCur, (int)(zString-zCur));
 11271  		}else{
 11272  			int c = zString[0];
 11273  			if( SyisUpper(c) ){
 11274  				c = SyToLower(zString[0]);
 11275  			}
 11276  			/* Append character */
 11277  			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 11278  			/* Advance the cursor */
 11279  			zString++;
 11280  		}
 11281  	}
 11282  	return JX9_OK;
 11283  }
 11284  /*
 11285   * string strtolower(string $str)
 11286   *  Make a string uppercase.
 11287   * Parameters
 11288   *  $str
 11289   *   The input string.
 11290   * Returns.
 11291   *  The uppercased string.
 11292   */
 11293  static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11294  {
 11295  	const char *zString, *zCur, *zEnd;
 11296  	int nLen;
 11297  	if( nArg < 1 ){
 11298  		/* Missing arguments, return null */
 11299  		jx9_result_null(pCtx);
 11300  		return JX9_OK;
 11301  	}
 11302  	/* Extract the target string */
 11303  	zString = jx9_value_to_string(apArg[0], &nLen);
 11304  	if( nLen < 1 ){
 11305  		/* Empty string, return */
 11306  		jx9_result_string(pCtx, "", 0);
 11307  		return JX9_OK;
 11308  	}
 11309  	/* Perform the requested operation */
 11310  	zEnd = &zString[nLen];
 11311  	for(;;){
 11312  		if( zString >= zEnd ){
 11313  			/* No more input, break immediately */
 11314  			break;
 11315  		}
 11316  		if( (unsigned char)zString[0] >= 0xc0 ){
 11317  			/* UTF-8 stream, output verbatim */
 11318  			zCur = zString;
 11319  			zString++;
 11320  			while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
 11321  				zString++;
 11322  			}
 11323  			/* Append UTF-8 stream */
 11324  			jx9_result_string(pCtx, zCur, (int)(zString-zCur));
 11325  		}else{
 11326  			int c = zString[0];
 11327  			if( SyisLower(c) ){
 11328  				c = SyToUpper(zString[0]);
 11329  			}
 11330  			/* Append character */
 11331  			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 11332  			/* Advance the cursor */
 11333  			zString++;
 11334  		}
 11335  	}
 11336  	return JX9_OK;
 11337  }
 11338  /*
 11339   * int ord(string $string)
 11340   *  Returns the ASCII value of the first character of string.
 11341   * Parameters
 11342   *  $str
 11343   *   The input string.
 11344   * Returns.
 11345   *  The ASCII value as an integer.
 11346   */
 11347  static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11348  {
 11349  	const char *zString;
 11350  	int nLen, c;
 11351  	if( nArg < 1 ){
 11352  		/* Missing arguments, return -1 */
 11353  		jx9_result_int(pCtx, -1);
 11354  		return JX9_OK;
 11355  	}
 11356  	/* Extract the target string */
 11357  	zString = jx9_value_to_string(apArg[0], &nLen);
 11358  	if( nLen < 1 ){
 11359  		/* Empty string, return -1 */
 11360  		jx9_result_int(pCtx, -1);
 11361  		return JX9_OK;
 11362  	}
 11363  	/* Extract the ASCII value of the first character */
 11364  	c = zString[0];
 11365  	/* Return that value */
 11366  	jx9_result_int(pCtx, c);
 11367  	return JX9_OK;
 11368  }
 11369  /*
 11370   * string chr(int $ascii)
 11371   *  Returns a one-character string containing the character specified by ascii.
 11372   * Parameters
 11373   *  $ascii
 11374   *   The ascii code.
 11375   * Returns.
 11376   *  The specified character.
 11377   */
 11378  static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11379  {
 11380  	int c;
 11381  	if( nArg < 1 ){
 11382  		/* Missing arguments, return null */
 11383  		jx9_result_null(pCtx);
 11384  		return JX9_OK;
 11385  	}
 11386  	/* Extract the ASCII value */
 11387  	c = jx9_value_to_int(apArg[0]);
 11388  	/* Return the specified character */
 11389  	jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 11390  	return JX9_OK;
 11391  }
 11392  /*
 11393   * Binary to hex consumer callback.
 11394   * This callback is the default consumer used by the hash functions
 11395   * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
 11396   */
 11397  static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
 11398  {
 11399  	/* Append hex chunk verbatim */
 11400  	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
 11401  	return SXRET_OK;
 11402  }
 11403  /*
 11404   * string bin2hex(string $str)
 11405   *  Convert binary data into hexadecimal representation.
 11406   * Parameters
 11407   *  $str
 11408   *   The input string.
 11409   * Returns.
 11410   *  Returns the hexadecimal representation of the given string.
 11411   */
 11412  static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11413  {
 11414  	const char *zString;
 11415  	int nLen;
 11416  	if( nArg < 1 ){
 11417  		/* Missing arguments, return null */
 11418  		jx9_result_null(pCtx);
 11419  		return JX9_OK;
 11420  	}
 11421  	/* Extract the target string */
 11422  	zString = jx9_value_to_string(apArg[0], &nLen);
 11423  	if( nLen < 1 ){
 11424  		/* Empty string, return */
 11425  		jx9_result_string(pCtx, "", 0);
 11426  		return JX9_OK;
 11427  	}
 11428  	/* Perform the requested operation */
 11429  	SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
 11430  	return JX9_OK;
 11431  }
 11432  /* Search callback signature */
 11433  typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
 11434  /*
 11435   * Case-insensitive pattern match.
 11436   * Brute force is the default search method used here.
 11437   * This is due to the fact that brute-forcing works quite
 11438   * well for short/medium texts on modern hardware.
 11439   */
 11440  static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
 11441  {
 11442  	const char *zpIn = (const char *)pPattern;
 11443  	const char *zIn = (const char *)pText;
 11444  	const char *zpEnd = &zpIn[iPatLen];
 11445  	const char *zEnd = &zIn[nLen];
 11446  	const char *zPtr, *zPtr2;
 11447  	int c, d;
 11448  	if( iPatLen > nLen ){
 11449  		/* Don't bother processing */
 11450  		return SXERR_NOTFOUND;
 11451  	}
 11452  	for(;;){
 11453  		if( zIn >= zEnd ){
 11454  			break;
 11455  		}
 11456  		c = SyToLower(zIn[0]);
 11457  		d = SyToLower(zpIn[0]);
 11458  		if( c == d ){
 11459  			zPtr   = &zIn[1];
 11460  			zPtr2  = &zpIn[1];
 11461  			for(;;){
 11462  				if( zPtr2 >= zpEnd ){
 11463  					/* Pattern found */
 11464  					if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
 11465  					return SXRET_OK;
 11466  				}
 11467  				if( zPtr >= zEnd ){
 11468  					break;
 11469  				}
 11470  				c = SyToLower(zPtr[0]);
 11471  				d = SyToLower(zPtr2[0]);
 11472  				if( c != d ){
 11473  					break;
 11474  				}
 11475  				zPtr++; zPtr2++;
 11476  			}
 11477  		}
 11478  		zIn++;
 11479  	}
 11480  	/* Pattern not found */
 11481  	return SXERR_NOTFOUND;
 11482  }
 11483  /*
 11484   * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
 11485   *  Find the first occurrence of a string.
 11486   * Parameters
 11487   *  $haystack
 11488   *   The input string.
 11489   * $needle
 11490   *   Search pattern (must be a string).
 11491   * $before_needle
 11492   *   If TRUE, strstr() returns the part of the haystack before the first occurrence 
 11493   *   of the needle (excluding the needle).
 11494   * Return
 11495   *  Returns the portion of string, or FALSE if needle is not found.
 11496   */
 11497  static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11498  {
 11499  	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
 11500  	const char *zBlob, *zPattern;
 11501  	int nLen, nPatLen;
 11502  	sxu32 nOfft;
 11503  	sxi32 rc;
 11504  	if( nArg < 2 ){
 11505  		/* Missing arguments, return FALSE */
 11506  		jx9_result_bool(pCtx, 0);
 11507  		return JX9_OK;
 11508  	}
 11509  	/* Extract the needle and the haystack */
 11510  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11511  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11512  	nOfft = 0; /* cc warning */
 11513  	if( nLen > 0 && nPatLen > 0 ){
 11514  		int before = 0;
 11515  		/* Perform the lookup */
 11516  		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
 11517  		if( rc != SXRET_OK ){
 11518  			/* Pattern not found, return FALSE */
 11519  			jx9_result_bool(pCtx, 0);
 11520  			return JX9_OK;
 11521  		}
 11522  		/* Return the portion of the string */
 11523  		if( nArg > 2 ){
 11524  			before = jx9_value_to_int(apArg[2]);
 11525  		}
 11526  		if( before ){
 11527  			jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
 11528  		}else{
 11529  			jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
 11530  		}
 11531  	}else{
 11532  		jx9_result_bool(pCtx, 0);
 11533  	}
 11534  	return JX9_OK;
 11535  }
 11536  /*
 11537   * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
 11538   *  Case-insensitive strstr().
 11539   * Parameters
 11540   *  $haystack
 11541   *   The input string.
 11542   * $needle
 11543   *   Search pattern (must be a string).
 11544   * $before_needle
 11545   *   If TRUE, strstr() returns the part of the haystack before the first occurrence 
 11546   *   of the needle (excluding the needle).
 11547   * Return
 11548   *  Returns the portion of string, or FALSE if needle is not found.
 11549   */
 11550  static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11551  {
 11552  	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
 11553  	const char *zBlob, *zPattern;
 11554  	int nLen, nPatLen;
 11555  	sxu32 nOfft;
 11556  	sxi32 rc;
 11557  	if( nArg < 2 ){
 11558  		/* Missing arguments, return FALSE */
 11559  		jx9_result_bool(pCtx, 0);
 11560  		return JX9_OK;
 11561  	}
 11562  	/* Extract the needle and the haystack */
 11563  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11564  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11565  	nOfft = 0; /* cc warning */
 11566  	if( nLen > 0 && nPatLen > 0 ){
 11567  		int before = 0;
 11568  		/* Perform the lookup */
 11569  		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
 11570  		if( rc != SXRET_OK ){
 11571  			/* Pattern not found, return FALSE */
 11572  			jx9_result_bool(pCtx, 0);
 11573  			return JX9_OK;
 11574  		}
 11575  		/* Return the portion of the string */
 11576  		if( nArg > 2 ){
 11577  			before = jx9_value_to_int(apArg[2]);
 11578  		}
 11579  		if( before ){
 11580  			jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
 11581  		}else{
 11582  			jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
 11583  		}
 11584  	}else{
 11585  		jx9_result_bool(pCtx, 0);
 11586  	}
 11587  	return JX9_OK;
 11588  }
 11589  /*
 11590   * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
 11591   *  Returns the numeric position of the first occurrence of needle in the haystack string.
 11592   * Parameters
 11593   *  $haystack
 11594   *   The input string.
 11595   * $needle
 11596   *   Search pattern (must be a string).
 11597   * $offset
 11598   *   This optional offset parameter allows you to specify which character in haystack
 11599   *   to start searching. The position returned is still relative to the beginning
 11600   *   of haystack.
 11601   * Return
 11602   *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
 11603   */
 11604  static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11605  {
 11606  	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
 11607  	const char *zBlob, *zPattern;
 11608  	int nLen, nPatLen, nStart;
 11609  	sxu32 nOfft;
 11610  	sxi32 rc;
 11611  	if( nArg < 2 ){
 11612  		/* Missing arguments, return FALSE */
 11613  		jx9_result_bool(pCtx, 0);
 11614  		return JX9_OK;
 11615  	}
 11616  	/* Extract the needle and the haystack */
 11617  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11618  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11619  	nOfft = 0; /* cc warning */
 11620  	nStart = 0;
 11621  	/* Peek the starting offset if available */
 11622  	if( nArg > 2 ){
 11623  		nStart = jx9_value_to_int(apArg[2]);
 11624  		if( nStart < 0 ){
 11625  			nStart = -nStart;
 11626  		}
 11627  		if( nStart >= nLen ){
 11628  			/* Invalid offset */
 11629  			nStart = 0;
 11630  		}else{
 11631  			zBlob += nStart;
 11632  			nLen -= nStart;
 11633  		}
 11634  	}
 11635  	if( nLen > 0 && nPatLen > 0 ){
 11636  		/* Perform the lookup */
 11637  		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
 11638  		if( rc != SXRET_OK ){
 11639  			/* Pattern not found, return FALSE */
 11640  			jx9_result_bool(pCtx, 0);
 11641  			return JX9_OK;
 11642  		}
 11643  		/* Return the pattern position */
 11644  		jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
 11645  	}else{
 11646  		jx9_result_bool(pCtx, 0);
 11647  	}
 11648  	return JX9_OK;
 11649  }
 11650  /*
 11651   * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
 11652   *  Case-insensitive strpos.
 11653   * Parameters
 11654   *  $haystack
 11655   *   The input string.
 11656   * $needle
 11657   *   Search pattern (must be a string).
 11658   * $offset
 11659   *   This optional offset parameter allows you to specify which character in haystack
 11660   *   to start searching. The position returned is still relative to the beginning
 11661   *   of haystack.
 11662   * Return
 11663   *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
 11664   */
 11665  static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11666  {
 11667  	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
 11668  	const char *zBlob, *zPattern;
 11669  	int nLen, nPatLen, nStart;
 11670  	sxu32 nOfft;
 11671  	sxi32 rc;
 11672  	if( nArg < 2 ){
 11673  		/* Missing arguments, return FALSE */
 11674  		jx9_result_bool(pCtx, 0);
 11675  		return JX9_OK;
 11676  	}
 11677  	/* Extract the needle and the haystack */
 11678  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11679  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11680  	nOfft = 0; /* cc warning */
 11681  	nStart = 0;
 11682  	/* Peek the starting offset if available */
 11683  	if( nArg > 2 ){
 11684  		nStart = jx9_value_to_int(apArg[2]);
 11685  		if( nStart < 0 ){
 11686  			nStart = -nStart;
 11687  		}
 11688  		if( nStart >= nLen ){
 11689  			/* Invalid offset */
 11690  			nStart = 0;
 11691  		}else{
 11692  			zBlob += nStart;
 11693  			nLen -= nStart;
 11694  		}
 11695  	}
 11696  	if( nLen > 0 && nPatLen > 0 ){
 11697  		/* Perform the lookup */
 11698  		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
 11699  		if( rc != SXRET_OK ){
 11700  			/* Pattern not found, return FALSE */
 11701  			jx9_result_bool(pCtx, 0);
 11702  			return JX9_OK;
 11703  		}
 11704  		/* Return the pattern position */
 11705  		jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
 11706  	}else{
 11707  		jx9_result_bool(pCtx, 0);
 11708  	}
 11709  	return JX9_OK;
 11710  }
 11711  /*
 11712   * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
 11713   *  Find the numeric position of the last occurrence of needle in the haystack string.
 11714   * Parameters
 11715   *  $haystack
 11716   *   The input string.
 11717   * $needle
 11718   *   Search pattern (must be a string).
 11719   * $offset
 11720   *   If specified, search will start this number of characters counted from the beginning
 11721   *   of the string. If the value is negative, search will instead start from that many 
 11722   *   characters from the end of the string, searching backwards.
 11723   * Return
 11724   *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
 11725   */
 11726  static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11727  {
 11728  	const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
 11729  	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
 11730  	int nLen, nPatLen;
 11731  	sxu32 nOfft;
 11732  	sxi32 rc;
 11733  	if( nArg < 2 ){
 11734  		/* Missing arguments, return FALSE */
 11735  		jx9_result_bool(pCtx, 0);
 11736  		return JX9_OK;
 11737  	}
 11738  	/* Extract the needle and the haystack */
 11739  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11740  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11741  	/* Point to the end of the pattern */
 11742  	zPtr = &zBlob[nLen - 1];
 11743  	zEnd = &zBlob[nLen];
 11744  	/* Save the starting posistion */
 11745  	zStart = zBlob;
 11746  	nOfft = 0; /* cc warning */
 11747  	/* Peek the starting offset if available */
 11748  	if( nArg > 2 ){
 11749  		int nStart;
 11750  		nStart = jx9_value_to_int(apArg[2]);
 11751  		if( nStart < 0 ){
 11752  			nStart = -nStart;
 11753  			if( nStart >= nLen ){
 11754  				/* Invalid offset */
 11755  				jx9_result_bool(pCtx, 0);
 11756  				return JX9_OK;
 11757  			}else{
 11758  				nLen -= nStart;
 11759  				zPtr = &zBlob[nLen - 1];
 11760  				zEnd = &zBlob[nLen];
 11761  			}
 11762  		}else{
 11763  			if( nStart >= nLen ){
 11764  				/* Invalid offset */
 11765  				jx9_result_bool(pCtx, 0);
 11766  				return JX9_OK;
 11767  			}else{
 11768  				zBlob += nStart;
 11769  				nLen -= nStart;
 11770  			}
 11771  		}
 11772  	}
 11773  	if( nLen > 0 && nPatLen > 0 ){
 11774  		/* Perform the lookup */
 11775  		for(;;){
 11776  			if( zBlob >= zPtr ){
 11777  				break;
 11778  			}
 11779  			rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
 11780  			if( rc == SXRET_OK ){
 11781  				/* Pattern found, return it's position */
 11782  				jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
 11783  				return JX9_OK;
 11784  			}
 11785  			zPtr--;
 11786  		}
 11787  		/* Pattern not found, return FALSE */
 11788  		jx9_result_bool(pCtx, 0);
 11789  	}else{
 11790  		jx9_result_bool(pCtx, 0);
 11791  	}
 11792  	return JX9_OK;
 11793  }
 11794  /*
 11795   * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
 11796   *  Case-insensitive strrpos.
 11797   * Parameters
 11798   *  $haystack
 11799   *   The input string.
 11800   * $needle
 11801   *   Search pattern (must be a string).
 11802   * $offset
 11803   *   If specified, search will start this number of characters counted from the beginning
 11804   *   of the string. If the value is negative, search will instead start from that many 
 11805   *   characters from the end of the string, searching backwards.
 11806   * Return
 11807   *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
 11808   */
 11809  static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11810  {
 11811  	const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
 11812  	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
 11813  	int nLen, nPatLen;
 11814  	sxu32 nOfft;
 11815  	sxi32 rc;
 11816  	if( nArg < 2 ){
 11817  		/* Missing arguments, return FALSE */
 11818  		jx9_result_bool(pCtx, 0);
 11819  		return JX9_OK;
 11820  	}
 11821  	/* Extract the needle and the haystack */
 11822  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11823  	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
 11824  	/* Point to the end of the pattern */
 11825  	zPtr = &zBlob[nLen - 1];
 11826  	zEnd = &zBlob[nLen];
 11827  	/* Save the starting posistion */
 11828  	zStart = zBlob;
 11829  	nOfft = 0; /* cc warning */
 11830  	/* Peek the starting offset if available */
 11831  	if( nArg > 2 ){
 11832  		int nStart;
 11833  		nStart = jx9_value_to_int(apArg[2]);
 11834  		if( nStart < 0 ){
 11835  			nStart = -nStart;
 11836  			if( nStart >= nLen ){
 11837  				/* Invalid offset */
 11838  				jx9_result_bool(pCtx, 0);
 11839  				return JX9_OK;
 11840  			}else{
 11841  				nLen -= nStart;
 11842  				zPtr = &zBlob[nLen - 1];
 11843  				zEnd = &zBlob[nLen];
 11844  			}
 11845  		}else{
 11846  			if( nStart >= nLen ){
 11847  				/* Invalid offset */
 11848  				jx9_result_bool(pCtx, 0);
 11849  				return JX9_OK;
 11850  			}else{
 11851  				zBlob += nStart;
 11852  				nLen -= nStart;
 11853  			}
 11854  		}
 11855  	}
 11856  	if( nLen > 0 && nPatLen > 0 ){
 11857  		/* Perform the lookup */
 11858  		for(;;){
 11859  			if( zBlob >= zPtr ){
 11860  				break;
 11861  			}
 11862  			rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
 11863  			if( rc == SXRET_OK ){
 11864  				/* Pattern found, return it's position */
 11865  				jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
 11866  				return JX9_OK;
 11867  			}
 11868  			zPtr--;
 11869  		}
 11870  		/* Pattern not found, return FALSE */
 11871  		jx9_result_bool(pCtx, 0);
 11872  	}else{
 11873  		jx9_result_bool(pCtx, 0);
 11874  	}
 11875  	return JX9_OK;
 11876  }
 11877  /*
 11878   * int strrchr(string $haystack, mixed $needle)
 11879   *  Find the last occurrence of a character in a string.
 11880   * Parameters
 11881   *  $haystack
 11882   *   The input string.
 11883   * $needle
 11884   *  If needle contains more than one character, only the first is used.
 11885   *  This behavior is different from that of strstr().
 11886   *  If needle is not a string, it is converted to an integer and applied
 11887   *  as the ordinal value of a character.
 11888   * Return
 11889   *  This function returns the portion of string, or FALSE if needle is not found.
 11890   */
 11891  static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11892  {
 11893  	const char *zBlob;
 11894  	int nLen, c;
 11895  	if( nArg < 2 ){
 11896  		/* Missing arguments, return FALSE */
 11897  		jx9_result_bool(pCtx, 0);
 11898  		return JX9_OK;
 11899  	}
 11900  	/* Extract the haystack */
 11901  	zBlob = jx9_value_to_string(apArg[0], &nLen);
 11902  	c = 0; /* cc warning */
 11903  	if( nLen > 0 ){
 11904  		sxu32 nOfft;
 11905  		sxi32 rc;
 11906  		if( jx9_value_is_string(apArg[1]) ){
 11907  			const char *zPattern;
 11908  			zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
 11909  														 * for NULL pointer.
 11910  														 */
 11911  			c = zPattern[0];
 11912  		}else{
 11913  			/* Int cast */
 11914  			c = jx9_value_to_int(apArg[1]);
 11915  		}
 11916  		/* Perform the lookup */
 11917  		rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
 11918  		if( rc != SXRET_OK ){
 11919  			/* No such entry, return FALSE */
 11920  			jx9_result_bool(pCtx, 0);
 11921  			return JX9_OK;
 11922  		}
 11923  		/* Return the string portion */
 11924  		jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
 11925  	}else{
 11926  		jx9_result_bool(pCtx, 0);
 11927  	}
 11928  	return JX9_OK;
 11929  }
 11930  /*
 11931   * string strrev(string $string)
 11932   *  Reverse a string.
 11933   * Parameters
 11934   *  $string
 11935   *   String to be reversed.
 11936   * Return
 11937   *  The reversed string.
 11938   */
 11939  static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11940  {
 11941  	const char *zIn, *zEnd;
 11942  	int nLen, c;
 11943  	if( nArg < 1 ){
 11944  		/* Missing arguments, return NULL */
 11945  		jx9_result_null(pCtx);
 11946  		return JX9_OK;
 11947  	}
 11948  	/* Extract the target string */
 11949  	zIn = jx9_value_to_string(apArg[0], &nLen);
 11950  	if( nLen < 1 ){
 11951  		/* Empty string Return null */
 11952  		jx9_result_null(pCtx);
 11953  		return JX9_OK;
 11954  	}
 11955  	/* Perform the requested operation */
 11956  	zEnd = &zIn[nLen - 1];
 11957  	for(;;){
 11958  		if( zEnd < zIn ){
 11959  			/* No more input to process */
 11960  			break;
 11961  		}
 11962  		/* Append current character */
 11963  		c = zEnd[0];
 11964  		jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 11965  		zEnd--;
 11966  	}
 11967  	return JX9_OK;
 11968  }
 11969  /*
 11970   * string str_repeat(string $input, int $multiplier)
 11971   *  Returns input repeated multiplier times.
 11972   * Parameters
 11973   *  $string
 11974   *   String to be repeated.
 11975   * $multiplier
 11976   *  Number of time the input string should be repeated.
 11977   *  multiplier has to be greater than or equal to 0. If the multiplier is set
 11978   *  to 0, the function will return an empty string.
 11979   * Return
 11980   *  The repeated string.
 11981   */
 11982  static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
 11983  {
 11984  	const char *zIn;
 11985  	int nLen, nMul;
 11986  	int rc;
 11987  	if( nArg < 2 ){
 11988  		/* Missing arguments, return NULL */
 11989  		jx9_result_null(pCtx);
 11990  		return JX9_OK;
 11991  	}
 11992  	/* Extract the target string */
 11993  	zIn = jx9_value_to_string(apArg[0], &nLen);
 11994  	if( nLen < 1 ){
 11995  		/* Empty string.Return null */
 11996  		jx9_result_null(pCtx);
 11997  		return JX9_OK;
 11998  	}
 11999  	/* Extract the multiplier */
 12000  	nMul = jx9_value_to_int(apArg[1]);
 12001  	if( nMul < 1 ){
 12002  		/* Return the empty string */
 12003  		jx9_result_string(pCtx, "", 0);
 12004  		return JX9_OK;
 12005  	}
 12006  	/* Perform the requested operation */
 12007  	for(;;){
 12008  		if( nMul < 1 ){
 12009  			break;
 12010  		}
 12011  		/* Append the copy */
 12012  		rc = jx9_result_string(pCtx, zIn, nLen);
 12013  		if( rc != JX9_OK ){
 12014  			/* Out of memory, break immediately */
 12015  			break;
 12016  		}
 12017  		nMul--;
 12018  	}
 12019  	return JX9_OK;
 12020  }
 12021  /*
 12022   * string nl2br(string $string[, bool $is_xhtml = true ])
 12023   *  Inserts HTML line breaks before all newlines in a string.
 12024   * Parameters
 12025   *  $string
 12026   *   The input string.
 12027   * $is_xhtml
 12028   *   Whenever to use XHTML compatible line breaks or not.
 12029   * Return
 12030   *  The processed string.
 12031   */
 12032  static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12033  {
 12034  	const char *zIn, *zCur, *zEnd;
 12035  	int is_xhtml = 0;
 12036  	int nLen;
 12037  	if( nArg < 1 ){
 12038  		/* Missing arguments, return the empty string */
 12039  		jx9_result_string(pCtx, "", 0);
 12040  		return JX9_OK;
 12041  	}
 12042  	/* Extract the target string */
 12043  	zIn = jx9_value_to_string(apArg[0], &nLen);
 12044  	if( nLen < 1 ){
 12045  		/* Empty string, return null */
 12046  		jx9_result_null(pCtx);
 12047  		return JX9_OK;
 12048  	}
 12049  	if( nArg > 1 ){
 12050  		is_xhtml = jx9_value_to_bool(apArg[1]);
 12051  	}
 12052  	zEnd = &zIn[nLen];
 12053  	/* Perform the requested operation */
 12054  	for(;;){
 12055  		zCur = zIn;
 12056  		/* Delimit the string */
 12057  		while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
 12058  			zIn++;
 12059  		}
 12060  		if( zCur < zIn ){
 12061  			/* Output chunk verbatim */
 12062  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 12063  		}
 12064  		if( zIn >= zEnd ){
 12065  			/* No more input to process */
 12066  			break;
 12067  		}
 12068  		/* Output the HTML line break */
 12069  		if( is_xhtml ){
 12070  			jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
 12071  		}else{
 12072  			jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
 12073  		}
 12074  		zCur = zIn;
 12075  		/* Append trailing line */
 12076  		while( zIn < zEnd && (zIn[0] == '\n'  || zIn[0] == '\r') ){
 12077  			zIn++;
 12078  		}
 12079  		if( zCur < zIn ){
 12080  			/* Output chunk verbatim */
 12081  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 12082  		}
 12083  	}
 12084  	return JX9_OK;
 12085  }
 12086  /*
 12087   * Format a given string and invoke the given callback on each processed chunk.
 12088   *  According to the JX9 reference manual.
 12089   * The format string is composed of zero or more directives: ordinary characters
 12090   * (excluding %) that are copied directly to the result, and conversion 
 12091   * specifications, each of which results in fetching its own parameter.
 12092   * This applies to both sprintf() and printf().
 12093   * Each conversion specification consists of a percent sign (%), followed by one
 12094   * or more of these elements, in order:
 12095   *   An optional sign specifier that forces a sign (- or +) to be used on a number.
 12096   *   By default, only the - sign is used on a number if it's negative. This specifier forces
 12097   *   positive numbers to have the + sign attached as well.
 12098   *   An optional padding specifier that says what character will be used for padding
 12099   *   the results to the right string size. This may be a space character or a 0 (zero character).
 12100   *   The default is to pad with spaces. An alternate padding character can be specified by prefixing
 12101   *   it with a single quote ('). See the examples below.
 12102   *   An optional alignment specifier that says if the result should be left-justified or right-justified.
 12103   *   The default is right-justified; a - character here will make it left-justified.
 12104   *   An optional number, a width specifier that says how many characters (minimum) this conversion
 12105   *   should result in.
 12106   *   An optional precision specifier in the form of a period (`.') followed by an optional decimal
 12107   *   digit string that says how many decimal digits should be displayed for floating-point numbers.
 12108   *   When using this specifier on a string, it acts as a cutoff point, setting a maximum character
 12109   *   limit to the string.
 12110   *  A type specifier that says what type the argument data should be treated as. Possible types:
 12111   *       % - a literal percent character. No argument is required.
 12112   *       b - the argument is treated as an integer, and presented as a binary number.
 12113   *       c - the argument is treated as an integer, and presented as the character with that ASCII value.
 12114   *       d - the argument is treated as an integer, and presented as a (signed) decimal number.
 12115   *       e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands 
 12116   * 	     for the number of digits after the decimal point.
 12117   *       E - like %e but uses uppercase letter (e.g. 1.2E+2).
 12118   *       u - the argument is treated as an integer, and presented as an unsigned decimal number.
 12119   *       f - the argument is treated as a float, and presented as a floating-point number (locale aware).
 12120   *       F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
 12121   *       g - shorter of %e and %f.
 12122   *       G - shorter of %E and %f.
 12123   *       o - the argument is treated as an integer, and presented as an octal number.
 12124   *       s - the argument is treated as and presented as a string.
 12125   *       x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
 12126   *       X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
 12127   */
 12128  /*
 12129   * This implementation is based on the one found in the SQLite3 source tree.
 12130   */
 12131  #define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
 12132  /*
 12133  ** Conversion types fall into various categories as defined by the
 12134  ** following enumeration.
 12135  */
 12136  #define JX9_FMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
 12137  #define JX9_FMT_FLOAT       2 /* Floating point.%f */
 12138  #define JX9_FMT_EXP         3 /* Exponentional notation.%e and %E */
 12139  #define JX9_FMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
 12140  #define JX9_FMT_SIZE        5 /* Total number of characters processed so far.%n */
 12141  #define JX9_FMT_STRING      6 /* Strings.%s */
 12142  #define JX9_FMT_PERCENT     7 /* Percent symbol.%% */
 12143  #define JX9_FMT_CHARX       8 /* Characters.%c */
 12144  #define JX9_FMT_ERROR       9 /* Used to indicate no such conversion type */
 12145  /*
 12146  ** Allowed values for jx9_fmt_info.flags
 12147  */
 12148  #define JX9_FMT_FLAG_SIGNED	  0x01
 12149  #define JX9_FMT_FLAG_UNSIGNED 0x02
 12150  /*
 12151  ** Each builtin conversion character (ex: the 'd' in "%d") is described
 12152  ** by an instance of the following structure
 12153  */
 12154  typedef struct jx9_fmt_info jx9_fmt_info;
 12155  struct jx9_fmt_info
 12156  {
 12157    char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
 12158    sxu8 base;     /* The base for radix conversion */
 12159    int flags;    /* One or more of JX9_FMT_FLAG_ constants below */
 12160    sxu8 type;     /* Conversion paradigm */
 12161    char *charset; /* The character set for conversion */
 12162    char *prefix;  /* Prefix on non-zero values in alt format */
 12163  };
 12164  #ifndef JX9_OMIT_FLOATING_POINT
 12165  /*
 12166  ** "*val" is a double such that 0.1 <= *val < 10.0
 12167  ** Return the ascii code for the leading digit of *val, then
 12168  ** multiply "*val" by 10.0 to renormalize.
 12169  **
 12170  ** Example:
 12171  **     input:     *val = 3.14159
 12172  **     output:    *val = 1.4159    function return = '3'
 12173  **
 12174  ** The counter *cnt is incremented each time.  After counter exceeds
 12175  ** 16 (the number of significant digits in a 64-bit float) '0' is
 12176  ** always returned.
 12177  */
 12178  static int vxGetdigit(sxlongreal *val, int *cnt)
 12179  {
 12180    sxlongreal d;
 12181    int digit;
 12182  
 12183    if( (*cnt)++ >= 16 ){
 12184  	  return '0';
 12185    }
 12186    digit = (int)*val;
 12187    d = digit;
 12188     *val = (*val - d)*10.0;
 12189    return digit + '0' ;
 12190  }
 12191  #endif /* JX9_OMIT_FLOATING_POINT */
 12192  /*
 12193   * The following table is searched linearly, so it is good to put the most frequently
 12194   * used conversion types first.
 12195   */
 12196  static const jx9_fmt_info aFmt[] = {
 12197    {  'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0    }, 
 12198    {  's',  0, 0, JX9_FMT_STRING,     0,                  0    }, 
 12199    {  'c',  0, 0, JX9_FMT_CHARX,      0,                  0    }, 
 12200    {  'x', 16, 0, JX9_FMT_RADIX,      "0123456789abcdef", "x0" }, 
 12201    {  'X', 16, 0, JX9_FMT_RADIX,      "0123456789ABCDEF", "X0" }, 
 12202    {  'b',  2, 0, JX9_FMT_RADIX,      "01",                "b0"}, 
 12203    {  'o',  8, 0, JX9_FMT_RADIX,      "01234567",         "0"  }, 
 12204    {  'u', 10, 0, JX9_FMT_RADIX,      "0123456789",       0    }, 
 12205    {  'f',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    }, 
 12206    {  'F',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    }, 
 12207    {  'e',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "e",    0    }, 
 12208    {  'E',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "E",    0    }, 
 12209    {  'g',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "e",    0    }, 
 12210    {  'G',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "E",    0    }, 
 12211    {  '%',  0, 0, JX9_FMT_PERCENT,    0,                  0    }
 12212  };
 12213  /*
 12214   * Format a given string.
 12215   * The root program.  All variations call this core.
 12216   * INPUTS:
 12217   *   xConsumer   This is a pointer to a function taking four arguments
 12218   *            1. A pointer to the call context.
 12219   *            2. A pointer to the list of characters to be output
 12220   *               (Note, this list is NOT null terminated.)
 12221   *            3. An integer number of characters to be output.
 12222   *               (Note: This number might be zero.)
 12223   *            4. Upper layer private data.
 12224   *   zIn       This is the format string, as in the usual print.
 12225   *   apArg     This is a pointer to a list of arguments.
 12226   */
 12227  JX9_PRIVATE sxi32 jx9InputFormat(
 12228  	int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
 12229  	jx9_context *pCtx,  /* call context */
 12230  	const char *zIn,    /* Format string */
 12231  	int nByte,          /* Format string length */
 12232  	int nArg,           /* Total argument of the given arguments */
 12233  	jx9_value **apArg,  /* User arguments */
 12234  	void *pUserData,    /* Last argument to xConsumer() */
 12235  	int vf              /* TRUE if called from vfprintf, vsprintf context */ 
 12236  	)
 12237  {
 12238  	char spaces[] = "                                                  ";
 12239  #define etSPACESIZE ((int)sizeof(spaces)-1)
 12240  	const char *zCur, *zEnd = &zIn[nByte];
 12241  	char *zBuf, zWorker[JX9_FMT_BUFSIZ];       /* Working buffer */
 12242  	const jx9_fmt_info *pInfo;  /* Pointer to the appropriate info structure */
 12243  	int flag_alternateform; /* True if "#" flag is present */
 12244  	int flag_leftjustify;   /* True if "-" flag is present */
 12245  	int flag_blanksign;     /* True if " " flag is present */
 12246  	int flag_plussign;      /* True if "+" flag is present */
 12247  	int flag_zeropad;       /* True if field width constant starts with zero */
 12248  	jx9_value *pArg;         /* Current processed argument */
 12249  	jx9_int64 iVal;
 12250  	int precision;           /* Precision of the current field */
 12251  	char *zExtra;  
 12252  	int c, rc, n;
 12253  	int length;              /* Length of the field */
 12254  	int prefix;
 12255  	sxu8 xtype;              /* Conversion paradigm */
 12256  	int width;               /* Width of the current field */
 12257  	int idx;
 12258  	n = (vf == TRUE) ? 0 : 1;
 12259  #define NEXT_ARG	( n < nArg ? apArg[n++] : 0 )
 12260  	/* Start the format process */
 12261  	for(;;){
 12262  		zCur = zIn;
 12263  		while( zIn < zEnd && zIn[0] != '%' ){
 12264  			zIn++;
 12265  		}
 12266  		if( zCur < zIn ){
 12267  			/* Consume chunk verbatim */
 12268  			rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
 12269  			if( rc == SXERR_ABORT ){
 12270  				/* Callback request an operation abort */
 12271  				break;
 12272  			}
 12273  		}
 12274  		if( zIn >= zEnd ){
 12275  			/* No more input to process, break immediately */
 12276  			break;
 12277  		}
 12278  		/* Find out what flags are present */
 12279  		flag_leftjustify = flag_plussign = flag_blanksign = 
 12280  			flag_alternateform = flag_zeropad = 0;
 12281  		zIn++; /* Jump the precent sign */
 12282  		do{
 12283  			c = zIn[0];
 12284  			switch( c ){
 12285  			case '-':   flag_leftjustify = 1;     c = 0;   break;
 12286  			case '+':   flag_plussign = 1;        c = 0;   break;
 12287  			case ' ':   flag_blanksign = 1;       c = 0;   break;
 12288  			case '#':   flag_alternateform = 1;   c = 0;   break;
 12289  			case '0':   flag_zeropad = 1;         c = 0;   break;
 12290  			case '\'':
 12291  				zIn++;
 12292  				if( zIn < zEnd ){
 12293  					/* An alternate padding character can be specified by prefixing it with a single quote (') */
 12294  					c = zIn[0];
 12295  					for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
 12296  						spaces[idx] = (char)c;
 12297  					}
 12298  					c = 0;
 12299  				}
 12300  				break;
 12301  			default:                                       break;
 12302  			}
 12303  		}while( c==0 && (zIn++ < zEnd) );
 12304  		/* Get the field width */
 12305  		width = 0;
 12306  		while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
 12307  			width = width*10 + (zIn[0] - '0');
 12308  			zIn++;
 12309  		}
 12310  		if( zIn < zEnd && zIn[0] == '$' ){
 12311  			/* Position specifer */
 12312  			if( width > 0 ){
 12313  				n = width;
 12314  				if( vf && n > 0 ){ 
 12315  					n--;
 12316  				}
 12317  			}
 12318  			zIn++;
 12319  			width = 0;
 12320  			if( zIn < zEnd && zIn[0] == '0' ){
 12321  				flag_zeropad = 1;
 12322  				zIn++;
 12323  			}
 12324  			while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
 12325  				width = width*10 + (zIn[0] - '0');
 12326  				zIn++;
 12327  			}
 12328  		}
 12329  		if( width > JX9_FMT_BUFSIZ-10 ){
 12330  			width = JX9_FMT_BUFSIZ-10;
 12331  		}
 12332  		/* Get the precision */
 12333  		precision = -1;
 12334  		if( zIn < zEnd && zIn[0] == '.' ){
 12335  			precision = 0;
 12336  			zIn++;
 12337  			while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
 12338  				precision = precision*10 + (zIn[0] - '0');
 12339  				zIn++;
 12340  			}
 12341  		}
 12342  		if( zIn >= zEnd ){
 12343  			/* No more input */
 12344  			break;
 12345  		}
 12346  		/* Fetch the info entry for the field */
 12347  		pInfo = 0;
 12348  		xtype = JX9_FMT_ERROR;
 12349  		c = zIn[0];
 12350  		zIn++; /* Jump the format specifer */
 12351  		for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
 12352  			if( c==aFmt[idx].fmttype ){
 12353  				pInfo = &aFmt[idx];
 12354  				xtype = pInfo->type;
 12355  				break;
 12356  			}
 12357  		}
 12358  		zBuf = zWorker; /* Point to the working buffer */
 12359  		length = 0;
 12360  		zExtra = 0;
 12361  		 /*
 12362  		  ** At this point, variables are initialized as follows:
 12363  		  **
 12364  		  **   flag_alternateform          TRUE if a '#' is present.
 12365  		  **   flag_plussign               TRUE if a '+' is present.
 12366  		  **   flag_leftjustify            TRUE if a '-' is present or if the
 12367  		  **                               field width was negative.
 12368  		  **   flag_zeropad                TRUE if the width began with 0.
 12369  		  **                               the conversion character.
 12370  		  **   flag_blanksign              TRUE if a ' ' is present.
 12371  		  **   width                       The specified field width.  This is
 12372  		  **                               always non-negative.  Zero is the default.
 12373  		  **   precision                   The specified precision.  The default
 12374  		  **                               is -1.
 12375  		  */
 12376  		switch(xtype){
 12377  		case JX9_FMT_PERCENT:
 12378  			/* A literal percent character */
 12379  			zWorker[0] = '%';
 12380  			length = (int)sizeof(char);
 12381  			break;
 12382  		case JX9_FMT_CHARX:
 12383  			/* The argument is treated as an integer, and presented as the character
 12384  			 * with that ASCII value
 12385  			 */
 12386  			pArg = NEXT_ARG;
 12387  			if( pArg == 0 ){
 12388  				c = 0;
 12389  			}else{
 12390  				c = jx9_value_to_int(pArg);
 12391  			}
 12392  			/* NUL byte is an acceptable value */
 12393  			zWorker[0] = (char)c;
 12394  			length = (int)sizeof(char);
 12395  			break;
 12396  		case JX9_FMT_STRING:
 12397  			/* the argument is treated as and presented as a string */
 12398  			pArg = NEXT_ARG;
 12399  			if( pArg == 0 ){
 12400  				length = 0;
 12401  			}else{
 12402  				zBuf = (char *)jx9_value_to_string(pArg, &length);
 12403  			}
 12404  			if( length < 1 ){
 12405  				zBuf = " ";
 12406  				length = (int)sizeof(char);
 12407  			}
 12408  			if( precision>=0 && precision<length ){
 12409  				length = precision;
 12410  			}
 12411  			if( flag_zeropad ){
 12412  				/* zero-padding works on strings too */
 12413  				for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
 12414  					spaces[idx] = '0';
 12415  				}
 12416  			}
 12417  			break;
 12418  		case JX9_FMT_RADIX:
 12419  			pArg = NEXT_ARG;
 12420  			if( pArg == 0 ){
 12421  				iVal = 0;
 12422  			}else{
 12423  				iVal = jx9_value_to_int64(pArg);
 12424  			}
 12425  			/* Limit the precision to prevent overflowing buf[] during conversion */
 12426  			if( precision>JX9_FMT_BUFSIZ-40 ){
 12427  				precision = JX9_FMT_BUFSIZ-40;
 12428  			}
 12429  #if 1
 12430          /* For the format %#x, the value zero is printed "0" not "0x0".
 12431          ** I think this is stupid.*/
 12432          if( iVal==0 ) flag_alternateform = 0;
 12433  #else
 12434          /* More sensible: turn off the prefix for octal (to prevent "00"), 
 12435          ** but leave the prefix for hex.*/
 12436          if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
 12437  #endif
 12438          if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
 12439            if( iVal<0 ){ 
 12440              iVal = -iVal;
 12441  			/* Ticket 1433-003 */
 12442  			if( iVal < 0 ){
 12443  				/* Overflow */
 12444  				iVal= 0x7FFFFFFFFFFFFFFF;
 12445  			}
 12446              prefix = '-';
 12447            }else if( flag_plussign )  prefix = '+';
 12448            else if( flag_blanksign )  prefix = ' ';
 12449            else                       prefix = 0;
 12450          }else{
 12451  			if( iVal<0 ){
 12452  				iVal = -iVal;
 12453  				/* Ticket 1433-003 */
 12454  				if( iVal < 0 ){
 12455  					/* Overflow */
 12456  					iVal= 0x7FFFFFFFFFFFFFFF;
 12457  				}
 12458  			}
 12459  			prefix = 0;
 12460  		}
 12461          if( flag_zeropad && precision<width-(prefix!=0) ){
 12462            precision = width-(prefix!=0);
 12463          }
 12464          zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
 12465          {
 12466            register char *cset;      /* Use registers for speed */
 12467            register int base;
 12468            cset = pInfo->charset;
 12469            base = pInfo->base;
 12470            do{                                           /* Convert to ascii */
 12471              *(--zBuf) = cset[iVal%base];
 12472              iVal = iVal/base;
 12473            }while( iVal>0 );
 12474          }
 12475          length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
 12476          for(idx=precision-length; idx>0; idx--){
 12477            *(--zBuf) = '0';                             /* Zero pad */
 12478          }
 12479          if( prefix ) *(--zBuf) = (char)prefix;               /* Add sign */
 12480          if( flag_alternateform && pInfo->prefix ){      /* Add "0" or "0x" */
 12481            char *pre, x;
 12482            pre = pInfo->prefix;
 12483            if( *zBuf!=pre[0] ){
 12484              for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
 12485            }
 12486          }
 12487          length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
 12488  		break;
 12489  		case JX9_FMT_FLOAT:
 12490  		case JX9_FMT_EXP:
 12491  		case JX9_FMT_GENERIC:{
 12492  #ifndef JX9_OMIT_FLOATING_POINT
 12493  		long double realvalue;
 12494  		int  exp;                /* exponent of real numbers */
 12495  		double rounder;          /* Used for rounding floating point values */
 12496  		int flag_dp;            /* True if decimal point should be shown */
 12497  		int flag_rtz;           /* True if trailing zeros should be removed */
 12498  		int flag_exp;           /* True to force display of the exponent */
 12499  		int nsd;                 /* Number of significant digits returned */
 12500  		pArg = NEXT_ARG;
 12501  		if( pArg == 0 ){
 12502  			realvalue = 0;
 12503  		}else{
 12504  			realvalue = jx9_value_to_double(pArg);
 12505  		}
 12506          if( precision<0 ) precision = 6;         /* Set default precision */
 12507          if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
 12508          if( realvalue<0.0 ){
 12509            realvalue = -realvalue;
 12510            prefix = '-';
 12511          }else{
 12512            if( flag_plussign )          prefix = '+';
 12513            else if( flag_blanksign )    prefix = ' ';
 12514            else                         prefix = 0;
 12515          }
 12516          if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
 12517          rounder = 0.0;
 12518  #if 0
 12519          /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
 12520          for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
 12521  #else
 12522          /* It makes more sense to use 0.5 */
 12523          for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
 12524  #endif
 12525          if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
 12526          /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
 12527          exp = 0;
 12528          if( realvalue>0.0 ){
 12529            while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
 12530            while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
 12531            while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
 12532            while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
 12533            if( exp>350 || exp<-350 ){
 12534              zBuf = "NaN";
 12535              length = 3;
 12536              break;
 12537            }
 12538          }
 12539          zBuf = zWorker;
 12540          /*
 12541          ** If the field type is etGENERIC, then convert to either etEXP
 12542          ** or etFLOAT, as appropriate.
 12543          */
 12544          flag_exp = xtype==JX9_FMT_EXP;
 12545          if( xtype!=JX9_FMT_FLOAT ){
 12546            realvalue += rounder;
 12547            if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
 12548          }
 12549          if( xtype==JX9_FMT_GENERIC ){
 12550            flag_rtz = !flag_alternateform;
 12551            if( exp<-4 || exp>precision ){
 12552              xtype = JX9_FMT_EXP;
 12553            }else{
 12554              precision = precision - exp;
 12555              xtype = JX9_FMT_FLOAT;
 12556            }
 12557          }else{
 12558            flag_rtz = 0;
 12559          }
 12560          /*
 12561          ** The "exp+precision" test causes output to be of type etEXP if
 12562          ** the precision is too large to fit in buf[].
 12563          */
 12564          nsd = 0;
 12565          if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
 12566            flag_dp = (precision>0 || flag_alternateform);
 12567            if( prefix ) *(zBuf++) = (char)prefix;         /* Sign */
 12568            if( exp<0 )  *(zBuf++) = '0';            /* Digits before "." */
 12569            else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
 12570            if( flag_dp ) *(zBuf++) = '.';           /* The decimal point */
 12571            for(exp++; exp<0 && precision>0; precision--, exp++){
 12572              *(zBuf++) = '0';
 12573            }
 12574            while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
 12575            *(zBuf--) = 0;                           /* Null terminate */
 12576            if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
 12577              while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
 12578              if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
 12579            }
 12580            zBuf++;                            /* point to next free slot */
 12581          }else{    /* etEXP or etGENERIC */
 12582            flag_dp = (precision>0 || flag_alternateform);
 12583            if( prefix ) *(zBuf++) = (char)prefix;   /* Sign */
 12584            *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);  /* First digit */
 12585            if( flag_dp ) *(zBuf++) = '.';     /* Decimal point */
 12586            while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
 12587            zBuf--;                            /* point to last digit */
 12588            if( flag_rtz && flag_dp ){          /* Remove tail zeros */
 12589              while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
 12590              if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
 12591            }
 12592            zBuf++;                            /* point to next free slot */
 12593            if( exp || flag_exp ){
 12594              *(zBuf++) = pInfo->charset[0];
 12595              if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
 12596              else       { *(zBuf++) = '+'; }
 12597              if( exp>=100 ){
 12598                *(zBuf++) = (char)((exp/100)+'0');                /* 100's digit */
 12599                exp %= 100;
 12600              }
 12601              *(zBuf++) = (char)(exp/10+'0');                     /* 10's digit */
 12602              *(zBuf++) = (char)(exp%10+'0');                     /* 1's digit */
 12603            }
 12604          }
 12605          /* The converted number is in buf[] and zero terminated.Output it.
 12606          ** Note that the number is in the usual order, not reversed as with
 12607          ** integer conversions.*/
 12608          length = (int)(zBuf-zWorker);
 12609          zBuf = zWorker;
 12610          /* Special case:  Add leading zeros if the flag_zeropad flag is
 12611          ** set and we are not left justified */
 12612          if( flag_zeropad && !flag_leftjustify && length < width){
 12613            int i;
 12614            int nPad = width - length;
 12615            for(i=width; i>=nPad; i--){
 12616              zBuf[i] = zBuf[i-nPad];
 12617            }
 12618            i = prefix!=0;
 12619            while( nPad-- ) zBuf[i++] = '0';
 12620            length = width;
 12621          }
 12622  #else
 12623           zBuf = " ";
 12624  		 length = (int)sizeof(char);
 12625  #endif /* JX9_OMIT_FLOATING_POINT */
 12626  		 break;
 12627  							 }
 12628  		default:
 12629  			/* Invalid format specifer */
 12630  			zWorker[0] = '?';
 12631  			length = (int)sizeof(char);
 12632  			break;
 12633  		}
 12634  		 /*
 12635  		 ** The text of the conversion is pointed to by "zBuf" and is
 12636  		 ** "length" characters long.The field width is "width".Do
 12637  		 ** the output.
 12638  		 */
 12639      if( !flag_leftjustify ){
 12640        register int nspace;
 12641        nspace = width-length;
 12642        if( nspace>0 ){
 12643          while( nspace>=etSPACESIZE ){
 12644  			rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
 12645  			if( rc != SXRET_OK ){
 12646  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 12647  			}
 12648  			nspace -= etSPACESIZE;
 12649          }
 12650          if( nspace>0 ){
 12651  			rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
 12652  			if( rc != SXRET_OK ){
 12653  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 12654  			}
 12655  		}
 12656        }
 12657      }
 12658      if( length>0 ){
 12659  		rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
 12660  		if( rc != SXRET_OK ){
 12661  		  return SXERR_ABORT; /* Consumer routine request an operation abort */
 12662  		}
 12663      }
 12664      if( flag_leftjustify ){
 12665        register int nspace;
 12666        nspace = width-length;
 12667        if( nspace>0 ){
 12668          while( nspace>=etSPACESIZE ){
 12669  			rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
 12670  			if( rc != SXRET_OK ){
 12671  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 12672  			}
 12673  			nspace -= etSPACESIZE;
 12674          }
 12675          if( nspace>0 ){
 12676  			rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
 12677  			if( rc != SXRET_OK ){
 12678  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 12679  			}
 12680  		}
 12681        }
 12682      }
 12683   }/* for(;;) */
 12684  	return SXRET_OK;
 12685  }
 12686  /*
 12687   * Callback [i.e: Formatted input consumer] of the sprintf function.
 12688   */
 12689  static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
 12690  {
 12691  	/* Consume directly */
 12692  	jx9_result_string(pCtx, zInput, nLen);
 12693  	SXUNUSED(pUserData); /* cc warning */
 12694  	return JX9_OK;
 12695  }
 12696  /*
 12697   * string sprintf(string $format[, mixed $args [, mixed $... ]])
 12698   *  Return a formatted string.
 12699   * Parameters
 12700   *  $format 
 12701   *    The format string (see block comment above)
 12702   * Return
 12703   *  A string produced according to the formatting string format. 
 12704   */
 12705  static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12706  {
 12707  	const char *zFormat;
 12708  	int nLen;
 12709  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 12710  		/* Missing/Invalid arguments, return the empty string */
 12711  		jx9_result_string(pCtx, "", 0);
 12712  		return JX9_OK;
 12713  	}
 12714  	/* Extract the string format */
 12715  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 12716  	if( nLen < 1 ){
 12717  		/* Empty string */
 12718  		jx9_result_string(pCtx, "", 0);
 12719  		return JX9_OK;
 12720  	}
 12721  	/* Format the string */
 12722  	jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
 12723  	return JX9_OK;
 12724  }
 12725  /*
 12726   * Callback [i.e: Formatted input consumer] of the printf function.
 12727   */
 12728  static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
 12729  {
 12730  	jx9_int64 *pCounter = (jx9_int64 *)pUserData;
 12731  	/* Call the VM output consumer directly */
 12732  	jx9_context_output(pCtx, zInput, nLen);
 12733  	/* Increment counter */
 12734  	*pCounter += nLen;
 12735  	return JX9_OK;
 12736  }
 12737  /*
 12738   * int64 printf(string $format[, mixed $args[, mixed $... ]])
 12739   *  Output a formatted string.
 12740   * Parameters
 12741   *  $format
 12742   *   See sprintf() for a description of format.
 12743   * Return
 12744   *  The length of the outputted string.
 12745   */
 12746  static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12747  {
 12748  	jx9_int64 nCounter = 0;
 12749  	const char *zFormat;
 12750  	int nLen;
 12751  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 12752  		/* Missing/Invalid arguments, return 0 */
 12753  		jx9_result_int(pCtx, 0);
 12754  		return JX9_OK;
 12755  	}
 12756  	/* Extract the string format */
 12757  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 12758  	if( nLen < 1 ){
 12759  		/* Empty string */
 12760  		jx9_result_int(pCtx, 0);
 12761  		return JX9_OK;
 12762  	}
 12763  	/* Format the string */
 12764  	jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
 12765  	/* Return the length of the outputted string */
 12766  	jx9_result_int64(pCtx, nCounter);
 12767  	return JX9_OK;
 12768  }
 12769  /*
 12770   * int vprintf(string $format, array $args)
 12771   *  Output a formatted string.
 12772   * Parameters
 12773   *  $format
 12774   *   See sprintf() for a description of format.
 12775   * Return
 12776   *  The length of the outputted string.
 12777   */
 12778  static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12779  {
 12780  	jx9_int64 nCounter = 0;
 12781  	const char *zFormat;
 12782  	jx9_hashmap *pMap;
 12783  	SySet sArg;
 12784  	int nLen, n;
 12785  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
 12786  		/* Missing/Invalid arguments, return 0 */
 12787  		jx9_result_int(pCtx, 0);
 12788  		return JX9_OK;
 12789  	}
 12790  	/* Extract the string format */
 12791  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 12792  	if( nLen < 1 ){
 12793  		/* Empty string */
 12794  		jx9_result_int(pCtx, 0);
 12795  		return JX9_OK;
 12796  	}
 12797  	/* Point to the hashmap */
 12798  	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
 12799  	/* Extract arguments from the hashmap */
 12800  	n = jx9HashmapValuesToSet(pMap, &sArg);
 12801  	/* Format the string */
 12802  	jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
 12803  	/* Return the length of the outputted string */
 12804  	jx9_result_int64(pCtx, nCounter);
 12805  	/* Release the container */
 12806  	SySetRelease(&sArg);
 12807  	return JX9_OK;
 12808  }
 12809  /*
 12810   * int vsprintf(string $format, array $args)
 12811   *  Output a formatted string.
 12812   * Parameters
 12813   *  $format
 12814   *   See sprintf() for a description of format.
 12815   * Return
 12816   *  A string produced according to the formatting string format.
 12817   */
 12818  static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12819  {
 12820  	const char *zFormat;
 12821  	jx9_hashmap *pMap;
 12822  	SySet sArg;
 12823  	int nLen, n;
 12824  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
 12825  		/* Missing/Invalid arguments, return the empty string */
 12826  		jx9_result_string(pCtx, "", 0);
 12827  		return JX9_OK;
 12828  	}
 12829  	/* Extract the string format */
 12830  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 12831  	if( nLen < 1 ){
 12832  		/* Empty string */
 12833  		jx9_result_string(pCtx, "", 0);
 12834  		return JX9_OK;
 12835  	}
 12836  	/* Point to hashmap */
 12837  	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
 12838  	/* Extract arguments from the hashmap */
 12839  	n = jx9HashmapValuesToSet(pMap, &sArg);
 12840  	/* Format the string */
 12841  	jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
 12842  	/* Release the container */
 12843  	SySetRelease(&sArg);
 12844  	return JX9_OK;
 12845  }
 12846  /*
 12847   * string size_format(int64 $size)
 12848   *  Return a smart string represenation of the given size [i.e: 64-bit integer]
 12849   *  Example:
 12850   *    print size_format(1*1024*1024*1024);// 1GB
 12851   *    print size_format(512*1024*1024); // 512 MB
 12852   *    print size_format(file_size(/path/to/my/file_8192)); //8KB
 12853   * Parameter
 12854   *  $size
 12855   *    Entity size in bytes.
 12856   * Return
 12857   *   Formatted string representation of the given size.
 12858   */
 12859  static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12860  {
 12861  	/*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
 12862  	static const char zUnit[] = {"KMGTPEZ"};
 12863  	sxi32 nRest, i_32;
 12864  	jx9_int64 iSize;
 12865  	int c = -1; /* index in zUnit[] */
 12866  
 12867  	if( nArg < 1 ){
 12868  		/* Missing argument, return the empty string */
 12869  		jx9_result_string(pCtx, "", 0);
 12870  		return JX9_OK;
 12871  	}
 12872  	/* Extract the given size */
 12873  	iSize = jx9_value_to_int64(apArg[0]);
 12874  	if( iSize < 100 /* Bytes */ ){
 12875  		/* Don't bother formatting, return immediately */
 12876  		jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
 12877  		return JX9_OK;
 12878  	}
 12879  	for(;;){
 12880  		nRest = (sxi32)(iSize & 0x3FF); 
 12881  		iSize >>= 10;
 12882  		c++;
 12883  		if( (iSize & (~0 ^ 1023)) == 0 ){
 12884  			break;
 12885  		}
 12886  	}
 12887  	nRest /= 100;
 12888  	if( nRest > 9 ){
 12889  		nRest = 9;
 12890  	}
 12891  	if( iSize > 999 ){
 12892  		c++;
 12893  		nRest = 9;
 12894  		iSize = 0;
 12895  	}
 12896  	i_32 = (sxi32)iSize;
 12897  	/* Format */
 12898  	jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
 12899  	return JX9_OK;
 12900  }
 12901  #if !defined(JX9_DISABLE_HASH_FUNC)
 12902  /*
 12903   * string md5(string $str[, bool $raw_output = false])
 12904   *   Calculate the md5 hash of a string.
 12905   * Parameter
 12906   *  $str
 12907   *   Input string
 12908   * $raw_output
 12909   *   If the optional raw_output is set to TRUE, then the md5 digest
 12910   *   is instead returned in raw binary format with a length of 16.
 12911   * Return
 12912   *  MD5 Hash as a 32-character hexadecimal string.
 12913   */
 12914  static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12915  {
 12916  	unsigned char zDigest[16];
 12917  	int raw_output = FALSE;
 12918  	const void *pIn;
 12919  	int nLen;
 12920  	if( nArg < 1 ){
 12921  		/* Missing arguments, return the empty string */
 12922  		jx9_result_string(pCtx, "", 0);
 12923  		return JX9_OK;
 12924  	}
 12925  	/* Extract the input string */
 12926  	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
 12927  	if( nLen < 1 ){
 12928  		/* Empty string */
 12929  		jx9_result_string(pCtx, "", 0);
 12930  		return JX9_OK;
 12931  	}
 12932  	if( nArg > 1 && jx9_value_is_bool(apArg[1])){
 12933  		raw_output = jx9_value_to_bool(apArg[1]);
 12934  	}
 12935  	/* Compute the MD5 digest */
 12936  	SyMD5Compute(pIn, (sxu32)nLen, zDigest);
 12937  	if( raw_output ){
 12938  		/* Output raw digest */
 12939  		jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
 12940  	}else{
 12941  		/* Perform a binary to hex conversion */
 12942  		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
 12943  	}
 12944  	return JX9_OK;
 12945  }
 12946  /*
 12947   * string sha1(string $str[, bool $raw_output = false])
 12948   *   Calculate the sha1 hash of a string.
 12949   * Parameter
 12950   *  $str
 12951   *   Input string
 12952   * $raw_output
 12953   *   If the optional raw_output is set to TRUE, then the md5 digest
 12954   *   is instead returned in raw binary format with a length of 16.
 12955   * Return
 12956   *  SHA1 Hash as a 40-character hexadecimal string.
 12957   */
 12958  static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
 12959  {
 12960  	unsigned char zDigest[20];
 12961  	int raw_output = FALSE;
 12962  	const void *pIn;
 12963  	int nLen;
 12964  	if( nArg < 1 ){
 12965  		/* Missing arguments, return the empty string */
 12966  		jx9_result_string(pCtx, "", 0);
 12967  		return JX9_OK;
 12968  	}
 12969  	/* Extract the input string */
 12970  	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
 12971  	if( nLen < 1 ){
 12972  		/* Empty string */
 12973  		jx9_result_string(pCtx, "", 0);
 12974  		return JX9_OK;
 12975  	}
 12976  	if( nArg > 1 && jx9_value_is_bool(apArg[1])){
 12977  		raw_output = jx9_value_to_bool(apArg[1]);
 12978  	}
 12979  	/* Compute the SHA1 digest */
 12980  	SySha1Compute(pIn, (sxu32)nLen, zDigest);
 12981  	if( raw_output ){
 12982  		/* Output raw digest */
 12983  		jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
 12984  	}else{
 12985  		/* Perform a binary to hex conversion */
 12986  		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
 12987  	}
 12988  	return JX9_OK;
 12989  }
 12990  /*
 12991   * int64 crc32(string $str)
 12992   *   Calculates the crc32 polynomial of a strin.
 12993   * Parameter
 12994   *  $str
 12995   *   Input string
 12996   * Return
 12997   *  CRC32 checksum of the given input (64-bit integer).
 12998   */
 12999  static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13000  {
 13001  	const void *pIn;
 13002  	sxu32 nCRC;
 13003  	int nLen;
 13004  	if( nArg < 1 ){
 13005  		/* Missing arguments, return 0 */
 13006  		jx9_result_int(pCtx, 0);
 13007  		return JX9_OK;
 13008  	}
 13009  	/* Extract the input string */
 13010  	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
 13011  	if( nLen < 1 ){
 13012  		/* Empty string */
 13013  		jx9_result_int(pCtx, 0);
 13014  		return JX9_OK;
 13015  	}
 13016  	/* Calculate the sum */
 13017  	nCRC = SyCrc32(pIn, (sxu32)nLen);
 13018  	/* Return the CRC32 as 64-bit integer */
 13019  	jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
 13020  	return JX9_OK;
 13021  }
 13022  #endif /* JX9_DISABLE_HASH_FUNC */
 13023  /*
 13024   * Parse a CSV string and invoke the supplied callback for each processed xhunk.
 13025   */
 13026  JX9_PRIVATE sxi32 jx9ProcessCsv(
 13027  	const char *zInput, /* Raw input */
 13028  	int nByte,  /* Input length */
 13029  	int delim,  /* Delimiter */
 13030  	int encl,   /* Enclosure */
 13031  	int escape,  /* Escape character */
 13032  	sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
 13033  	void *pUserData /* Last argument to xConsumer() */
 13034  	)
 13035  {
 13036  	const char *zEnd = &zInput[nByte];
 13037  	const char *zIn = zInput;
 13038  	const char *zPtr;
 13039  	int isEnc;
 13040  	/* Start processing */
 13041  	for(;;){
 13042  		if( zIn >= zEnd ){
 13043  			/* No more input to process */
 13044  			break;
 13045  		}
 13046  		isEnc = 0;
 13047  		zPtr = zIn;
 13048  		/* Find the first delimiter */
 13049  		while( zIn < zEnd ){
 13050  			if( zIn[0] == delim && !isEnc){
 13051  				/* Delimiter found, break imediately */
 13052  				break;
 13053  			}else if( zIn[0] == encl ){
 13054  				/* Inside enclosure? */
 13055  				isEnc = !isEnc;
 13056  			}else if( zIn[0] == escape ){
 13057  				/* Escape sequence */
 13058  				zIn++;
 13059  			}
 13060  			/* Advance the cursor */
 13061  			zIn++;
 13062  		}
 13063  		if( zIn > zPtr ){
 13064  			int nByte = (int)(zIn-zPtr);
 13065  			sxi32 rc;
 13066  			/* Invoke the supllied callback */
 13067  			if( zPtr[0] == encl ){
 13068  				zPtr++;
 13069  				nByte-=2;
 13070  			}
 13071  			if( nByte > 0 ){
 13072  				rc = xConsumer(zPtr, nByte, pUserData);
 13073  				if( rc == SXERR_ABORT ){
 13074  					/* User callback request an operation abort */
 13075  					break;
 13076  				}
 13077  			}
 13078  		}
 13079  		/* Ignore trailing delimiter */
 13080  		while( zIn < zEnd && zIn[0] == delim ){
 13081  			zIn++;
 13082  		}
 13083  	}
 13084  	return SXRET_OK;
 13085  }
 13086  /*
 13087   * Default consumer callback for the CSV parsing routine defined above.
 13088   * All the processed input is insereted into an array passed as the last
 13089   * argument to this callback.
 13090   */
 13091  JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
 13092  {
 13093  	jx9_value *pArray = (jx9_value *)pUserData;
 13094  	jx9_value sEntry;
 13095  	SyString sToken;
 13096  	/* Insert the token in the given array */
 13097  	SyStringInitFromBuf(&sToken, zToken, nTokenLen);
 13098  	/* Remove trailing and leading white spcaces and null bytes */
 13099  	SyStringFullTrimSafe(&sToken);
 13100  	if( sToken.nByte < 1){
 13101  		return SXRET_OK;
 13102  	}
 13103  	jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
 13104  	jx9_array_add_elem(pArray, 0, &sEntry);
 13105  	jx9MemObjRelease(&sEntry);
 13106  	return SXRET_OK;
 13107  }
 13108  /*
 13109   * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
 13110   *  Parse a CSV string into an array.
 13111   * Parameters
 13112   *  $input
 13113   *   The string to parse.
 13114   *  $delimiter
 13115   *   Set the field delimiter (one character only).
 13116   *  $enclosure
 13117   *   Set the field enclosure character (one character only).
 13118   *  $escape
 13119   *   Set the escape character (one character only). Defaults as a backslash (\)
 13120   * Return
 13121   *  An indexed array containing the CSV fields or NULL on failure.
 13122   */
 13123  static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13124  {
 13125  	const char *zInput, *zPtr;
 13126  	jx9_value *pArray;
 13127  	int delim  = ',';   /* Delimiter */
 13128  	int encl   = '"' ;  /* Enclosure */
 13129  	int escape = '\\';  /* Escape character */
 13130  	int nLen;
 13131  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 13132  		/* Missing/Invalid arguments, return NULL */
 13133  		jx9_result_null(pCtx);
 13134  		return JX9_OK;
 13135  	}
 13136  	/* Extract the raw input */
 13137  	zInput = jx9_value_to_string(apArg[0], &nLen);
 13138  	if( nArg > 1 ){
 13139  		int i;
 13140  		if( jx9_value_is_string(apArg[1]) ){
 13141  			/* Extract the delimiter */
 13142  			zPtr = jx9_value_to_string(apArg[1], &i);
 13143  			if( i > 0 ){
 13144  				delim = zPtr[0];
 13145  			}
 13146  		}
 13147  		if( nArg > 2 ){
 13148  			if( jx9_value_is_string(apArg[2]) ){
 13149  				/* Extract the enclosure */
 13150  				zPtr = jx9_value_to_string(apArg[2], &i);
 13151  				if( i > 0 ){
 13152  					encl = zPtr[0];
 13153  				}
 13154  			}
 13155  			if( nArg > 3 ){
 13156  				if( jx9_value_is_string(apArg[3]) ){
 13157  					/* Extract the escape character */
 13158  					zPtr = jx9_value_to_string(apArg[3], &i);
 13159  					if( i > 0 ){
 13160  						escape = zPtr[0];
 13161  					}
 13162  				}
 13163  			}
 13164  		}
 13165  	}
 13166  	/* Create our array */
 13167  	pArray = jx9_context_new_array(pCtx);
 13168  	if( pArray == 0 ){
 13169  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 13170  		jx9_result_null(pCtx);
 13171  		return JX9_OK;
 13172  	}
 13173  	/* Parse the raw input */
 13174  	jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
 13175  	/* Return the freshly created array */
 13176  	jx9_result_value(pCtx, pArray);
 13177  	return JX9_OK;
 13178  }
 13179  /*
 13180   * Extract a tag name from a raw HTML input and insert it in the given
 13181   * container.
 13182   * Refer to [strip_tags()].
 13183   */
 13184  static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
 13185  {
 13186  	const char *zEnd = &zTag[nByte];
 13187  	const char *zPtr;
 13188  	SyString sEntry;
 13189  	/* Strip tags */
 13190  	for(;;){
 13191  		while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
 13192  			|| zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
 13193  				zTag++;
 13194  		}
 13195  		if( zTag >= zEnd ){
 13196  			break;
 13197  		}
 13198  		zPtr = zTag;
 13199  		/* Delimit the tag */
 13200  		while(zTag < zEnd ){
 13201  			if( (unsigned char)zTag[0] >= 0xc0 ){
 13202  				/* UTF-8 stream */
 13203  				zTag++;
 13204  				SX_JMP_UTF8(zTag, zEnd);
 13205  			}else if( !SyisAlphaNum(zTag[0]) ){
 13206  				break;
 13207  			}else{
 13208  				zTag++;
 13209  			}
 13210  		}
 13211  		if( zTag > zPtr ){
 13212  			/* Perform the insertion */
 13213  			SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
 13214  			SyStringFullTrim(&sEntry);
 13215  			SySetPut(pSet, (const void *)&sEntry);
 13216  		}
 13217  		/* Jump the trailing '>' */
 13218  		zTag++;
 13219  	}
 13220  	return SXRET_OK;
 13221  }
 13222  /*
 13223   * Check if the given HTML tag name is present in the given container.
 13224   * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
 13225   * Refer to [strip_tags()].
 13226   */
 13227  static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
 13228  {
 13229  	if( SySetUsed(pSet) > 0 ){
 13230  		const char *zCur, *zEnd = &zTag[nByte];
 13231  		SyString sTag;
 13232  		while( zTag < zEnd &&  (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
 13233  			((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
 13234  			zTag++;
 13235  		}
 13236  		/* Delimit the tag */
 13237  		zCur = zTag;
 13238  		while(zTag < zEnd ){
 13239  			if( (unsigned char)zTag[0] >= 0xc0 ){
 13240  				/* UTF-8 stream */
 13241  				zTag++;
 13242  				SX_JMP_UTF8(zTag, zEnd);
 13243  			}else if( !SyisAlphaNum(zTag[0]) ){
 13244  				break;
 13245  			}else{
 13246  				zTag++;
 13247  			}
 13248  		}
 13249  		SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
 13250  		/* Trim leading white spaces and null bytes */
 13251  		SyStringLeftTrimSafe(&sTag);
 13252  		if( sTag.nByte > 0 ){
 13253  			SyString *aEntry, *pEntry;
 13254  			sxi32 rc;
 13255  			sxu32 n;
 13256  			/* Perform the lookup */
 13257  			aEntry = (SyString *)SySetBasePtr(pSet);
 13258  			for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
 13259  				pEntry = &aEntry[n];
 13260  				/* Do the comparison */
 13261  				rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
 13262  				if( !rc ){
 13263  					return SXRET_OK;
 13264  				}
 13265  			}
 13266  		}
 13267  	}
 13268  	/* No such tag */
 13269  	return SXERR_NOTFOUND;
 13270  }
 13271  /*
 13272   * This function tries to return a string [i.e: in the call context result buffer]
 13273   * with all NUL bytes, HTML and JX9 tags stripped from a given string.
 13274   * Refer to [strip_tags()].
 13275   */
 13276  JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
 13277  {
 13278  	const char *zEnd = &zIn[nByte];
 13279  	const char *zPtr, *zTag;
 13280  	SySet sSet;
 13281  	/* initialize the set of allowed tags */
 13282  	SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
 13283  	if( nTaglen > 0 ){
 13284  		/* Set of allowed tags */
 13285  		AddTag(&sSet, zTaglist, nTaglen);
 13286  	}
 13287  	/* Set the empty string */
 13288  	jx9_result_string(pCtx, "", 0);
 13289  	/* Start processing */
 13290  	for(;;){
 13291  		if(zIn >= zEnd){
 13292  			/* No more input to process */
 13293  			break;
 13294  		}
 13295  		zPtr = zIn;
 13296  		/* Find a tag */
 13297  		while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
 13298  			zIn++;
 13299  		}
 13300  		if( zIn > zPtr ){
 13301  			/* Consume raw input */
 13302  			jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
 13303  		}
 13304  		/* Ignore trailing null bytes */
 13305  		while( zIn < zEnd && zIn[0] == 0 ){
 13306  			zIn++;
 13307  		}
 13308  		if(zIn >= zEnd){
 13309  			/* No more input to process */
 13310  			break;
 13311  		}
 13312  		if( zIn[0] == '<' ){
 13313  			sxi32 rc;
 13314  			zTag = zIn++;
 13315  			/* Delimit the tag */
 13316  			while( zIn < zEnd && zIn[0] != '>' ){
 13317  				zIn++;
 13318  			}
 13319  			if( zIn < zEnd ){
 13320  				zIn++; /* Ignore the trailing closing tag */
 13321  			}
 13322  			/* Query the set */
 13323  			rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
 13324  			if( rc == SXRET_OK ){
 13325  				/* Keep the tag */
 13326  				jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
 13327  			}
 13328  		}
 13329  	}
 13330  	/* Cleanup */
 13331  	SySetRelease(&sSet);
 13332  	return SXRET_OK;
 13333  }
 13334  /*
 13335   * string strip_tags(string $str[, string $allowable_tags])
 13336   *   Strip HTML and JX9 tags from a string.
 13337   * Parameters
 13338   *  $str
 13339   *  The input string.
 13340   * $allowable_tags
 13341   *  You can use the optional second parameter to specify tags which should not be stripped. 
 13342   * Return
 13343   *  Returns the stripped string.
 13344   */
 13345  static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13346  {
 13347  	const char *zTaglist = 0;
 13348  	const char *zString;
 13349  	int nTaglen = 0;
 13350  	int nLen;
 13351  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 13352  		/* Missing/Invalid arguments, return the empty string */
 13353  		jx9_result_string(pCtx, "", 0);
 13354  		return JX9_OK;
 13355  	}
 13356  	/* Point to the raw string */
 13357  	zString = jx9_value_to_string(apArg[0], &nLen);
 13358  	if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
 13359  		/* Allowed tag */
 13360  		zTaglist = jx9_value_to_string(apArg[1], &nTaglen);		
 13361  	}
 13362  	/* Process input */
 13363  	jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
 13364  	return JX9_OK;
 13365  }
 13366  /*
 13367   * array str_split(string $string[, int $split_length = 1 ])
 13368   *  Convert a string to an array.
 13369   * Parameters
 13370   * $str
 13371   *  The input string.
 13372   * $split_length
 13373   *  Maximum length of the chunk.
 13374   * Return
 13375   *  If the optional split_length parameter is specified, the returned array
 13376   *  will be broken down into chunks with each being split_length in length, otherwise
 13377   *  each chunk will be one character in length. FALSE is returned if split_length is less than 1.
 13378   *  If the split_length length exceeds the length of string, the entire string is returned 
 13379   *  as the first (and only) array element.
 13380   */
 13381  static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13382  {
 13383  	const char *zString, *zEnd;
 13384  	jx9_value *pArray, *pValue;
 13385  	int split_len;
 13386  	int nLen;
 13387  	if( nArg < 1 ){
 13388  		/* Missing arguments, return FALSE */
 13389  		jx9_result_bool(pCtx, 0);
 13390  		return JX9_OK;
 13391  	}
 13392  	/* Point to the target string */
 13393  	zString = jx9_value_to_string(apArg[0], &nLen);
 13394  	if( nLen < 1 ){
 13395  		/* Nothing to process, return FALSE */
 13396  		jx9_result_bool(pCtx, 0);
 13397  		return JX9_OK;
 13398  	}
 13399  	split_len = (int)sizeof(char);
 13400  	if( nArg > 1 ){
 13401  		/* Split length */
 13402  		split_len = jx9_value_to_int(apArg[1]);
 13403  		if( split_len < 1 ){
 13404  			/* Invalid length, return FALSE */
 13405  			jx9_result_bool(pCtx, 0);
 13406  			return JX9_OK;
 13407  		}
 13408  		if( split_len > nLen ){
 13409  			split_len = nLen;
 13410  		}
 13411  	}
 13412  	/* Create the array and the scalar value */
 13413  	pArray = jx9_context_new_array(pCtx);
 13414  	/*Chunk value */
 13415  	pValue = jx9_context_new_scalar(pCtx);
 13416  	if( pValue == 0 || pArray == 0 ){
 13417  		/* Return FALSE */
 13418  		jx9_result_bool(pCtx, 0);
 13419  		return JX9_OK;
 13420  	}
 13421  	/* Point to the end of the string */
 13422  	zEnd = &zString[nLen];
 13423  	/* Perform the requested operation */
 13424  	for(;;){
 13425  		int nMax;
 13426  		if( zString >= zEnd ){
 13427  			/* No more input to process */
 13428  			break;
 13429  		}
 13430  		nMax = (int)(zEnd-zString);
 13431  		if( nMax < split_len ){
 13432  			split_len = nMax;
 13433  		}
 13434  		/* Copy the current chunk */
 13435  		jx9_value_string(pValue, zString, split_len);
 13436  		/* Insert it */
 13437  		jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
 13438  		/* reset the string cursor */
 13439  		jx9_value_reset_string_cursor(pValue);
 13440  		/* Update position */
 13441  		zString += split_len;
 13442  	}
 13443  	/* 
 13444  	 * Return the array.
 13445  	 * Don't worry about freeing memory, everything will be automatically released
 13446  	 * upon we return from this function.
 13447  	 */
 13448  	jx9_result_value(pCtx, pArray);
 13449  	return JX9_OK;
 13450  }
 13451  /*
 13452   * Tokenize a raw string and extract the first non-space token.
 13453   * Refer to [strspn()].
 13454   */
 13455  static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
 13456  {
 13457  	const char *zIn = *pzIn;
 13458  	const char *zPtr;
 13459  	/* Ignore leading white spaces */
 13460  	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
 13461  		zIn++;
 13462  	}
 13463  	if( zIn >= zEnd ){
 13464  		/* End of input */
 13465  		return SXERR_EOF;
 13466  	}
 13467  	zPtr = zIn;
 13468  	/* Extract the token */
 13469  	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
 13470  		zIn++;
 13471  	}
 13472  	SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
 13473  	/* Synchronize pointers */
 13474  	*pzIn = zIn;
 13475  	/* Return to the caller */
 13476  	return SXRET_OK;
 13477  }
 13478  /*
 13479   * Check if the given string contains only characters from the given mask.
 13480   * return the longest match.
 13481   * Refer to [strspn()].
 13482   */
 13483  static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
 13484  {
 13485  	const char *zEnd = &zString[nLen];
 13486  	const char *zIn = zString;
 13487  	int i, c;
 13488  	for(;;){
 13489  		if( zString >= zEnd ){
 13490  			break;
 13491  		}
 13492  		/* Extract current character */
 13493  		c = zString[0];
 13494  		/* Perform the lookup */
 13495  		for( i = 0 ; i < nMaskLen ; i++ ){
 13496  			if( c == zMask[i] ){
 13497  				/* Character found */
 13498  				break;
 13499  			}
 13500  		}
 13501  		if( i >= nMaskLen ){
 13502  			/* Character not in the current mask, break immediately */
 13503  			break;
 13504  		}
 13505  		/* Advance cursor */
 13506  		zString++;
 13507  	}
 13508  	/* Longest match */
 13509  	return (int)(zString-zIn);
 13510  }
 13511  /*
 13512   * Do the reverse operation of the previous function [i.e: LongestStringMask()].
 13513   * Refer to [strcspn()].
 13514   */
 13515  static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
 13516  {
 13517  	const char *zEnd = &zString[nLen];
 13518  	const char *zIn = zString;
 13519  	int i, c;
 13520  	for(;;){
 13521  		if( zString >= zEnd ){
 13522  			break;
 13523  		}
 13524  		/* Extract current character */
 13525  		c = zString[0];
 13526  		/* Perform the lookup */
 13527  		for( i = 0 ; i < nMaskLen ; i++ ){
 13528  			if( c == zMask[i] ){
 13529  				break;
 13530  			}
 13531  		}
 13532  		if( i < nMaskLen ){
 13533  			/* Character in the current mask, break immediately */
 13534  			break;
 13535  		}
 13536  		/* Advance cursor */
 13537  		zString++;
 13538  	}
 13539  	/* Longest match */
 13540  	return (int)(zString-zIn);
 13541  }
 13542  /*
 13543   * int strspn(string $str, string $mask[, int $start[, int $length]])
 13544   *  Finds the length of the initial segment of a string consisting entirely
 13545   *  of characters contained within a given mask.
 13546   * Parameters
 13547   * $str
 13548   *  The input string.
 13549   * $mask
 13550   *  The list of allowable characters.
 13551   * $start
 13552   *  The position in subject to start searching.
 13553   *  If start is given and is non-negative, then strspn() will begin examining 
 13554   *  subject at the start'th position. For instance, in the string 'abcdef', the character
 13555   *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
 13556   *  If start is given and is negative, then strspn() will begin examining subject at the
 13557   *  start'th position from the end of subject.
 13558   * $length
 13559   *  The length of the segment from subject to examine.
 13560   *  If length is given and is non-negative, then subject will be examined for length
 13561   *  characters after the starting position.
 13562   *  If lengthis given and is negative, then subject will be examined from the starting
 13563   *  position up to length characters from the end of subject.
 13564   * Return
 13565   * Returns the length of the initial segment of subject which consists entirely of characters
 13566   * in mask.
 13567   */
 13568  static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13569  {
 13570  	const char *zString, *zMask, *zEnd;
 13571  	int iMasklen, iLen;
 13572  	SyString sToken;
 13573  	int iCount = 0;
 13574  	int rc;
 13575  	if( nArg < 2 ){
 13576  		/* Missing agruments, return zero */
 13577  		jx9_result_int(pCtx, 0);
 13578  		return JX9_OK;
 13579  	}
 13580  	/* Extract the target string */
 13581  	zString = jx9_value_to_string(apArg[0], &iLen);
 13582  	/* Extract the mask */
 13583  	zMask = jx9_value_to_string(apArg[1], &iMasklen);
 13584  	if( iLen < 1 || iMasklen < 1 ){
 13585  		/* Nothing to process, return zero */
 13586  		jx9_result_int(pCtx, 0);
 13587  		return JX9_OK;
 13588  	}
 13589  	if( nArg > 2 ){
 13590  		int nOfft;
 13591  		/* Extract the offset */
 13592  		nOfft = jx9_value_to_int(apArg[2]);
 13593  		if( nOfft < 0 ){
 13594  			const char *zBase = &zString[iLen + nOfft];
 13595  			if( zBase > zString ){
 13596  				iLen = (int)(&zString[iLen]-zBase);
 13597  				zString = zBase;	
 13598  			}else{
 13599  				/* Invalid offset */
 13600  				jx9_result_int(pCtx, 0);
 13601  				return JX9_OK;
 13602  			}
 13603  		}else{
 13604  			if( nOfft >= iLen ){
 13605  				/* Invalid offset */
 13606  				jx9_result_int(pCtx, 0);
 13607  				return JX9_OK;
 13608  			}else{
 13609  				/* Update offset */
 13610  				zString += nOfft;
 13611  				iLen -= nOfft;
 13612  			}
 13613  		}
 13614  		if( nArg > 3 ){
 13615  			int iUserlen;
 13616  			/* Extract the desired length */
 13617  			iUserlen = jx9_value_to_int(apArg[3]);
 13618  			if( iUserlen > 0 && iUserlen < iLen ){
 13619  				iLen = iUserlen;
 13620  			}
 13621  		}
 13622  	}
 13623  	/* Point to the end of the string */
 13624  	zEnd = &zString[iLen];
 13625  	/* Extract the first non-space token */
 13626  	rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
 13627  	if( rc == SXRET_OK && sToken.nByte > 0 ){
 13628  		/* Compare against the current mask */
 13629  		iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
 13630  	}
 13631  	/* Longest match */
 13632  	jx9_result_int(pCtx, iCount);
 13633  	return JX9_OK;
 13634  }
 13635  /*
 13636   * int strcspn(string $str, string $mask[, int $start[, int $length]])
 13637   *  Find length of initial segment not matching mask.
 13638   * Parameters
 13639   * $str
 13640   *  The input string.
 13641   * $mask
 13642   *  The list of not allowed characters.
 13643   * $start
 13644   *  The position in subject to start searching.
 13645   *  If start is given and is non-negative, then strspn() will begin examining 
 13646   *  subject at the start'th position. For instance, in the string 'abcdef', the character
 13647   *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
 13648   *  If start is given and is negative, then strspn() will begin examining subject at the
 13649   *  start'th position from the end of subject.
 13650   * $length
 13651   *  The length of the segment from subject to examine.
 13652   *  If length is given and is non-negative, then subject will be examined for length
 13653   *  characters after the starting position.
 13654   *  If lengthis given and is negative, then subject will be examined from the starting
 13655   *  position up to length characters from the end of subject.
 13656   * Return
 13657   *  Returns the length of the segment as an integer.
 13658   */
 13659  static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13660  {
 13661  	const char *zString, *zMask, *zEnd;
 13662  	int iMasklen, iLen;
 13663  	SyString sToken;
 13664  	int iCount = 0;
 13665  	int rc;
 13666  	if( nArg < 2 ){
 13667  		/* Missing agruments, return zero */
 13668  		jx9_result_int(pCtx, 0);
 13669  		return JX9_OK;
 13670  	}
 13671  	/* Extract the target string */
 13672  	zString = jx9_value_to_string(apArg[0], &iLen);
 13673  	/* Extract the mask */
 13674  	zMask = jx9_value_to_string(apArg[1], &iMasklen);
 13675  	if( iLen < 1 ){
 13676  		/* Nothing to process, return zero */
 13677  		jx9_result_int(pCtx, 0);
 13678  		return JX9_OK;
 13679  	}
 13680  	if( iMasklen < 1 ){
 13681  		/* No given mask, return the string length */
 13682  		jx9_result_int(pCtx, iLen);
 13683  		return JX9_OK;
 13684  	}
 13685  	if( nArg > 2 ){
 13686  		int nOfft;
 13687  		/* Extract the offset */
 13688  		nOfft = jx9_value_to_int(apArg[2]);
 13689  		if( nOfft < 0 ){
 13690  			const char *zBase = &zString[iLen + nOfft];
 13691  			if( zBase > zString ){
 13692  				iLen = (int)(&zString[iLen]-zBase);
 13693  				zString = zBase;	
 13694  			}else{
 13695  				/* Invalid offset */
 13696  				jx9_result_int(pCtx, 0);
 13697  				return JX9_OK;
 13698  			}
 13699  		}else{
 13700  			if( nOfft >= iLen ){
 13701  				/* Invalid offset */
 13702  				jx9_result_int(pCtx, 0);
 13703  				return JX9_OK;
 13704  			}else{
 13705  				/* Update offset */
 13706  				zString += nOfft;
 13707  				iLen -= nOfft;
 13708  			}
 13709  		}
 13710  		if( nArg > 3 ){
 13711  			int iUserlen;
 13712  			/* Extract the desired length */
 13713  			iUserlen = jx9_value_to_int(apArg[3]);
 13714  			if( iUserlen > 0 && iUserlen < iLen ){
 13715  				iLen = iUserlen;
 13716  			}
 13717  		}
 13718  	}
 13719  	/* Point to the end of the string */
 13720  	zEnd = &zString[iLen];
 13721  	/* Extract the first non-space token */
 13722  	rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
 13723  	if( rc == SXRET_OK && sToken.nByte > 0 ){
 13724  		/* Compare against the current mask */
 13725  		iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
 13726  	}
 13727  	/* Longest match */
 13728  	jx9_result_int(pCtx, iCount);
 13729  	return JX9_OK;
 13730  }
 13731  /*
 13732   * string strpbrk(string $haystack, string $char_list)
 13733   *  Search a string for any of a set of characters.
 13734   * Parameters
 13735   *  $haystack
 13736   *   The string where char_list is looked for.
 13737   *  $char_list
 13738   *   This parameter is case sensitive.
 13739   * Return
 13740   *  Returns a string starting from the character found, or FALSE if it is not found.
 13741   */
 13742  static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13743  {
 13744  	const char *zString, *zList, *zEnd;
 13745  	int iLen, iListLen, i, c;
 13746  	sxu32 nOfft, nMax;
 13747  	sxi32 rc;
 13748  	if( nArg < 2 ){
 13749  		/* Missing arguments, return FALSE */
 13750  		jx9_result_bool(pCtx, 0);
 13751  		return JX9_OK;
 13752  	}
 13753  	/* Extract the haystack and the char list */
 13754  	zString = jx9_value_to_string(apArg[0], &iLen);
 13755  	zList = jx9_value_to_string(apArg[1], &iListLen);
 13756  	if( iLen < 1 ){
 13757  		/* Nothing to process, return FALSE */
 13758  		jx9_result_bool(pCtx, 0);
 13759  		return JX9_OK;
 13760  	}
 13761  	/* Point to the end of the string */
 13762  	zEnd = &zString[iLen];
 13763  	nOfft = nMax = SXU32_HIGH;
 13764  	/* perform the requested operation */
 13765  	for( i = 0 ; i < iListLen ; i++ ){
 13766  		c = zList[i];
 13767  		rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
 13768  		if( rc == SXRET_OK ){
 13769  			if( nMax < nOfft ){
 13770  				nOfft = nMax;
 13771  			}
 13772  		}
 13773  	}
 13774  	if( nOfft == SXU32_HIGH ){
 13775  		/* No such substring, return FALSE */
 13776  		jx9_result_bool(pCtx, 0);
 13777  	}else{
 13778  		/* Return the substring */
 13779  		jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
 13780  	}
 13781  	return JX9_OK;
 13782  }
 13783  /*
 13784   * string soundex(string $str)
 13785   *  Calculate the soundex key of a string.
 13786   * Parameters
 13787   *  $str
 13788   *   The input string.
 13789   * Return
 13790   *  Returns the soundex key as a string.
 13791   * Note:
 13792   *  This implementation is based on the one found in the SQLite3
 13793   * source tree.
 13794   */
 13795  static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13796  {
 13797  	const unsigned char *zIn;
 13798  	char zResult[8];
 13799  	int i, j;
 13800  	static const unsigned char iCode[] = {
 13801  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 13802  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 13803  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 13804  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 13805  		0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 
 13806  		1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
 13807  		0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 
 13808  		1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
 13809  	};
 13810  	if( nArg < 1 ){
 13811  		/* Missing arguments, return the empty string */
 13812  		jx9_result_string(pCtx, "", 0);
 13813  		return JX9_OK;
 13814  	}
 13815  	zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
 13816  	for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
 13817  	if( zIn[i] ){
 13818  		unsigned char prevcode = iCode[zIn[i]&0x7f];
 13819  		zResult[0] = (char)SyToUpper(zIn[i]);
 13820  		for(j=1; j<4 && zIn[i]; i++){
 13821  			int code = iCode[zIn[i]&0x7f];
 13822  			if( code>0 ){
 13823  				if( code!=prevcode ){
 13824  					prevcode = (unsigned char)code;
 13825  					zResult[j++] = (char)code + '0';
 13826  				}
 13827  			}else{
 13828  				prevcode = 0;
 13829  			}
 13830  		}
 13831  		while( j<4 ){
 13832  			zResult[j++] = '0';
 13833  		}
 13834  		jx9_result_string(pCtx, zResult, 4);
 13835  	}else{
 13836  	  jx9_result_string(pCtx, "?000", 4);
 13837  	}
 13838  	return JX9_OK;
 13839  }
 13840  /*
 13841   * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
 13842   *  Wraps a string to a given number of characters.
 13843   * Parameters
 13844   *  $str
 13845   *   The input string.
 13846   * $width
 13847   *  The column width.
 13848   * $break
 13849   *  The line is broken using the optional break parameter.
 13850   * Return
 13851   *  Returns the given string wrapped at the specified column. 
 13852   */
 13853  static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13854  {
 13855  	const char *zIn, *zEnd, *zBreak;
 13856  	int iLen, iBreaklen, iChunk;
 13857  	if( nArg < 1 ){
 13858  		/* Missing arguments, return the empty string */
 13859  		jx9_result_string(pCtx, "", 0);
 13860  		return JX9_OK;
 13861  	}
 13862  	/* Extract the input string */
 13863  	zIn = jx9_value_to_string(apArg[0], &iLen);
 13864  	if( iLen < 1 ){
 13865  		/* Nothing to process, return the empty string */
 13866  		jx9_result_string(pCtx, "", 0);
 13867  		return JX9_OK;
 13868  	}
 13869  	/* Chunk length */
 13870  	iChunk = 75;
 13871  	iBreaklen = 0;
 13872  	zBreak = ""; /* cc warning */
 13873  	if( nArg > 1 ){
 13874  		iChunk = jx9_value_to_int(apArg[1]);
 13875  		if( iChunk < 1 ){
 13876  			iChunk = 75;
 13877  		}
 13878  		if( nArg > 2 ){
 13879  			zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
 13880  		}
 13881  	}
 13882  	if( iBreaklen < 1 ){
 13883  		/* Set a default column break */
 13884  #ifdef __WINNT__
 13885  		zBreak = "\r\n";
 13886  		iBreaklen = (int)sizeof("\r\n")-1;
 13887  #else
 13888  		zBreak = "\n";
 13889  		iBreaklen = (int)sizeof(char);
 13890  #endif
 13891  	}
 13892  	/* Perform the requested operation */
 13893  	zEnd = &zIn[iLen];
 13894  	for(;;){
 13895  		int nMax;
 13896  		if( zIn >= zEnd ){
 13897  			/* No more input to process */
 13898  			break;
 13899  		}
 13900  		nMax = (int)(zEnd-zIn);
 13901  		if( iChunk > nMax ){
 13902  			iChunk = nMax;
 13903  		}
 13904  		/* Append the column first */
 13905  		jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
 13906  		/* Advance the cursor */
 13907  		zIn += iChunk;
 13908  		if( zIn < zEnd ){
 13909  			/* Append the line break */
 13910  			jx9_result_string(pCtx, zBreak, iBreaklen);
 13911  		}
 13912  	}
 13913  	return JX9_OK;
 13914  }
 13915  /*
 13916   * Check if the given character is a member of the given mask.
 13917   * Return TRUE on success. FALSE otherwise.
 13918   * Refer to [strtok()].
 13919   */
 13920  static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
 13921  {
 13922  	int i;
 13923  	for( i = 0 ; i < nMasklen ; ++i ){
 13924  		if( c == zMask[i] ){
 13925  			if( pOfft ){
 13926  				*pOfft = i;
 13927  			}
 13928  			return TRUE;
 13929  		}
 13930  	}
 13931  	return FALSE;
 13932  }
 13933  /*
 13934   * Extract a single token from the input stream.
 13935   * Refer to [strtok()].
 13936   */
 13937  static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
 13938  {
 13939  	const char *zIn = *pzIn;
 13940  	const char *zPtr;
 13941  	/* Ignore leading delimiter */
 13942  	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
 13943  		zIn++;
 13944  	}
 13945  	if( zIn >= zEnd ){
 13946  		/* End of input */
 13947  		return SXERR_EOF;
 13948  	}
 13949  	zPtr = zIn;
 13950  	/* Extract the token */
 13951  	while( zIn < zEnd ){
 13952  		if( (unsigned char)zIn[0] >= 0xc0 ){
 13953  			/* UTF-8 stream */
 13954  			zIn++;
 13955  			SX_JMP_UTF8(zIn, zEnd);
 13956  		}else{
 13957  			if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
 13958  				break;
 13959  			}
 13960  			zIn++;
 13961  		}
 13962  	}
 13963  	SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
 13964  	/* Update the cursor */
 13965  	*pzIn = zIn;
 13966  	/* Return to the caller */
 13967  	return SXRET_OK;
 13968  }
 13969  /* strtok auxiliary private data */
 13970  typedef struct strtok_aux_data strtok_aux_data;
 13971  struct strtok_aux_data
 13972  {
 13973  	const char *zDup;  /* Complete duplicate of the input */
 13974  	const char *zIn;   /* Current input stream */
 13975  	const char *zEnd;  /* End of input */
 13976  };
 13977  /*
 13978   * string strtok(string $str, string $token)
 13979   * string strtok(string $token)
 13980   *  strtok() splits a string (str) into smaller strings (tokens), with each token
 13981   *  being delimited by any character from token. That is, if you have a string like
 13982   *  "This is an example string" you could tokenize this string into its individual
 13983   *  words by using the space character as the token.
 13984   *  Note that only the first call to strtok uses the string argument. Every subsequent
 13985   *  call to strtok only needs the token to use, as it keeps track of where it is in 
 13986   *  the current string. To start over, or to tokenize a new string you simply call strtok
 13987   *  with the string argument again to initialize it. Note that you may put multiple tokens
 13988   *  in the token parameter. The string will be tokenized when any one of the characters in 
 13989   *  the argument are found. 
 13990   * Parameters
 13991   *  $str
 13992   *  The string being split up into smaller strings (tokens).
 13993   * $token
 13994   *  The delimiter used when splitting up str.
 13995   * Return
 13996   *   Current token or FALSE on EOF.
 13997   */
 13998  static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
 13999  {
 14000  	strtok_aux_data *pAux;
 14001  	const char *zMask;
 14002  	SyString sToken; 
 14003  	int nMasklen;
 14004  	sxi32 rc;
 14005  	if( nArg < 2 ){
 14006  		/* Extract top aux data */
 14007  		pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
 14008  		if( pAux == 0 ){
 14009  			/* No aux data, return FALSE */
 14010  			jx9_result_bool(pCtx, 0);
 14011  			return JX9_OK;
 14012  		}
 14013  		nMasklen = 0;
 14014  		zMask = ""; /* cc warning */
 14015  		if( nArg > 0 ){
 14016  			/* Extract the mask */
 14017  			zMask = jx9_value_to_string(apArg[0], &nMasklen);
 14018  		}
 14019  		if( nMasklen < 1 ){
 14020  			/* Invalid mask, return FALSE */
 14021  			jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
 14022  			jx9_context_free_chunk(pCtx, pAux);
 14023  			(void)jx9_context_pop_aux_data(pCtx);
 14024  			jx9_result_bool(pCtx, 0);
 14025  			return JX9_OK;
 14026  		}
 14027  		/* Extract the token */
 14028  		rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
 14029  		if( rc != SXRET_OK ){
 14030  			/* EOF , discard the aux data */
 14031  			jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
 14032  			jx9_context_free_chunk(pCtx, pAux);
 14033  			(void)jx9_context_pop_aux_data(pCtx);
 14034  			jx9_result_bool(pCtx, 0);
 14035  		}else{
 14036  			/* Return the extracted token */
 14037  			jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
 14038  		}
 14039  	}else{
 14040  		const char *zInput, *zCur;
 14041  		char *zDup;
 14042  		int nLen;
 14043  		/* Extract the raw input */
 14044  		zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
 14045  		if( nLen < 1 ){
 14046  			/* Empty input, return FALSE */
 14047  			jx9_result_bool(pCtx, 0);
 14048  			return JX9_OK;
 14049  		}
 14050  		/* Extract the mask */
 14051  		zMask = jx9_value_to_string(apArg[1], &nMasklen);
 14052  		if( nMasklen < 1 ){
 14053  			/* Set a default mask */
 14054  #define TOK_MASK " \n\t\r\f" 
 14055  			zMask = TOK_MASK;
 14056  			nMasklen = (int)sizeof(TOK_MASK) - 1;
 14057  #undef TOK_MASK
 14058  		}
 14059  		/* Extract a single token */
 14060  		rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
 14061  		if( rc != SXRET_OK ){
 14062  			/* Empty input */
 14063  			jx9_result_bool(pCtx, 0);
 14064  			return JX9_OK;
 14065  		}else{
 14066  			/* Return the extracted token */
 14067  			jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
 14068  		}
 14069  		/* Create our auxilliary data and copy the input */
 14070  		pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
 14071  		if( pAux ){
 14072  			nLen -= (int)(zInput-zCur);
 14073  			if( nLen < 1 ){
 14074  				jx9_context_free_chunk(pCtx, pAux);
 14075  				return JX9_OK;
 14076  			}
 14077  			/* Duplicate input */
 14078  			zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
 14079  			if( zDup  ){
 14080  				SyMemcpy(zInput, zDup, (sxu32)nLen);
 14081  				/* Register the aux data */
 14082  				pAux->zDup = pAux->zIn = zDup;
 14083  				pAux->zEnd = &zDup[nLen];
 14084  				jx9_context_push_aux_data(pCtx, pAux);
 14085  			}
 14086  		}
 14087  	}
 14088  	return JX9_OK;
 14089  }
 14090  /*
 14091   * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
 14092   *  Pad a string to a certain length with another string
 14093   * Parameters
 14094   *  $input
 14095   *   The input string.
 14096   * $pad_length
 14097   *   If the value of pad_length is negative, less than, or equal to the length of the input 
 14098   *   string, no padding takes place.
 14099   * $pad_string
 14100   *   Note:
 14101   *    The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
 14102   *    divided by the pad_string's length.
 14103   * $pad_type
 14104   *    Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
 14105   *    is not specified it is assumed to be STR_PAD_RIGHT.
 14106   * Return
 14107   *  The padded string.
 14108   */
 14109  static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14110  {
 14111  	int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
 14112  	const char *zIn, *zPad;
 14113  	if( nArg < 2 ){
 14114  		/* Missing arguments, return the empty string */
 14115  		jx9_result_string(pCtx, "", 0);
 14116  		return JX9_OK;
 14117  	}
 14118  	/* Extract the target string */
 14119  	zIn = jx9_value_to_string(apArg[0], &iLen);
 14120  	/* Padding length */
 14121  	iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
 14122  	if( iPadlen > 0 ){
 14123  		iPadlen -= iLen;
 14124  	}
 14125  	if( iPadlen < 1  ){
 14126  		/* Return the string verbatim */
 14127  		jx9_result_string(pCtx, zIn, iLen);
 14128  		return JX9_OK;
 14129  	}
 14130  	zPad = " "; /* Whitespace padding */
 14131  	iStrpad = (int)sizeof(char);
 14132  	iType = 1 ; /* STR_PAD_RIGHT */
 14133  	if( nArg > 2 ){
 14134  		/* Padding string */
 14135  		zPad = jx9_value_to_string(apArg[2], &iStrpad);
 14136  		if( iStrpad < 1 ){
 14137  			/* Empty string */
 14138  			zPad = " "; /* Whitespace padding */
 14139  			iStrpad = (int)sizeof(char);
 14140  		}
 14141  		if( nArg > 3 ){
 14142  			/* Padd type */
 14143  			iType = jx9_value_to_int(apArg[3]);
 14144  			if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
 14145  				iType = 1 ; /* STR_PAD_RIGHT */
 14146  			}
 14147  		}
 14148  	}
 14149  	iDiv = 1;
 14150  	if( iType == 2 ){
 14151  		iDiv = 2; /* STR_PAD_BOTH */
 14152  	}
 14153  	/* Perform the requested operation */
 14154  	if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
 14155  		jPad = iStrpad;
 14156  		for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
 14157  			/* Padding */
 14158  			if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
 14159  				break;
 14160  			}
 14161  			jx9_result_string(pCtx, zPad, jPad);
 14162  		}
 14163  		if( iType == 0 /* STR_PAD_LEFT */ ){
 14164  			while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
 14165  				jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
 14166  				if( jPad > iStrpad ){
 14167  					jPad = iStrpad;
 14168  				}
 14169  				if( jPad < 1){
 14170  					break;
 14171  				}
 14172  				jx9_result_string(pCtx, zPad, jPad);
 14173  			}
 14174  		}
 14175  	}
 14176  	if( iLen > 0 ){
 14177  		/* Append the input string */
 14178  		jx9_result_string(pCtx, zIn, iLen);
 14179  	}
 14180  	if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
 14181  		for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
 14182  			/* Padding */
 14183  			if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
 14184  				break;
 14185  			}
 14186  			jx9_result_string(pCtx, zPad, iStrpad);
 14187  		}
 14188  		while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
 14189  			jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
 14190  			if( jPad > iStrpad ){
 14191  				jPad = iStrpad;
 14192  			}
 14193  			if( jPad < 1){
 14194  				break;
 14195  			}
 14196  			jx9_result_string(pCtx, zPad, jPad);
 14197  		}
 14198  	}
 14199  	return JX9_OK;
 14200  }
 14201  /*
 14202   * String replacement private data.
 14203   */
 14204  typedef struct str_replace_data str_replace_data;
 14205  struct str_replace_data
 14206  {
 14207  	/* The following two fields are only used by the strtr function */
 14208  	SyBlob *pWorker;         /* Working buffer */
 14209  	ProcStringMatch xMatch;  /* Pattern match routine */
 14210  	/* The following two fields are only used by the str_replace function */
 14211  	SySet *pCollector;  /* Argument collector*/
 14212  	jx9_context *pCtx;  /* Call context */
 14213  };
 14214  /*
 14215   * Remove a substring.
 14216   */
 14217  #define STRDEL(SRC, SLEN, OFFT, ILEN){\
 14218  	for(;;){\
 14219  		if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
 14220  	}\
 14221  }
 14222  /*
 14223   * Shift right and insert algorithm.
 14224   */
 14225  #define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
 14226  	sxu32 INLEN = LEN - OFFT;\
 14227  	for(;;){\
 14228  	  if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
 14229  	}\
 14230  	for(;;){\
 14231  		if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
 14232  	}\
 14233  } 
 14234  /*
 14235   * Replace all occurrences of the search string at offset (nOfft) with the given 
 14236   * replacement string [i.e: zReplace].
 14237   */
 14238  static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
 14239  {
 14240  	char *zInput = (char *)SyBlobData(pWorker);
 14241  	sxu32 n, m;
 14242  	n = SyBlobLength(pWorker);
 14243  	m = nOfft;
 14244  	/* Delete the old entry */
 14245  	STRDEL(zInput, n, m, nLen);
 14246  	SyBlobLength(pWorker) -= nLen;
 14247  	if( nReplen > 0 ){
 14248  		sxi32 iRep = nReplen;
 14249  		sxi32 rc;
 14250  		/*
 14251  		 * Make sure the working buffer is big enough to hold the replacement
 14252  		 * string.
 14253  		 */
 14254  		rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
 14255  		if( rc != SXRET_OK ){
 14256  			/* Simply ignore any memory failure problem */
 14257  			return SXRET_OK;
 14258  		}
 14259  		/* Perform the insertion now */
 14260  		zInput = (char *)SyBlobData(pWorker);
 14261  		n = SyBlobLength(pWorker);
 14262  		SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
 14263  		SyBlobLength(pWorker) += nReplen;
 14264  	}	
 14265  	return SXRET_OK;
 14266  }
 14267  /*
 14268   * String replacement walker callback.
 14269   * The following callback is invoked for each array entry that hold
 14270   * the replace string.
 14271   * Refer to the strtr() implementation for more information.
 14272   */
 14273  static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
 14274  {
 14275  	str_replace_data *pRepData = (str_replace_data *)pUserData;
 14276  	const char *zTarget, *zReplace;
 14277  	SyBlob *pWorker;
 14278  	int tLen, nLen;
 14279  	sxu32 nOfft;
 14280  	sxi32 rc;
 14281  	/* Point to the working buffer */
 14282  	pWorker = pRepData->pWorker;
 14283  	if( !jx9_value_is_string(pKey) ){
 14284  		/* Target and replace must be a string */
 14285  		return JX9_OK;
 14286  	}
 14287  	/* Extract the target and the replace */
 14288  	zTarget = jx9_value_to_string(pKey, &tLen);
 14289  	if( tLen < 1 ){
 14290  		/* Empty target, return immediately */
 14291  		return JX9_OK;
 14292  	}
 14293  	/* Perform a pattern search */
 14294  	rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
 14295  	if( rc != SXRET_OK ){
 14296  		/* Pattern not found */
 14297  		return JX9_OK;
 14298  	}
 14299  	/* Extract the replace string */
 14300  	zReplace = jx9_value_to_string(pData, &nLen);
 14301  	/* Perform the replace process */
 14302  	StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
 14303  	/* All done */
 14304  	return JX9_OK;
 14305  }
 14306  /*
 14307   * The following walker callback is invoked by the str_rplace() function inorder
 14308   * to collect search/replace string.
 14309   * This callback is invoked only if the given argument is of type array.
 14310   */
 14311  static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
 14312  {
 14313  	str_replace_data *pRep = (str_replace_data *)pUserData;
 14314  	SyString sWorker;
 14315  	const char *zIn;
 14316  	int nByte;
 14317  	/* Extract a string representation of the given argument */
 14318  	zIn = jx9_value_to_string(pData, &nByte);
 14319  	SyStringInitFromBuf(&sWorker, 0, 0);
 14320  	if( nByte > 0 ){
 14321  		char *zDup;
 14322  		/* Duplicate the chunk */
 14323  		zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE, 
 14324  			TRUE /* Release the chunk automatically, upon this context is destroyd */
 14325  			);
 14326  		if( zDup == 0 ){
 14327  			/* Ignore any memory failure problem */
 14328  			jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 14329  			return JX9_OK;
 14330  		}
 14331  		SyMemcpy(zIn, zDup, (sxu32)nByte);
 14332  		/* Save the chunk */
 14333  		SyStringInitFromBuf(&sWorker, zDup, nByte);
 14334  	}
 14335  	/* Save for later processing */
 14336  	SySetPut(pRep->pCollector, (const void *)&sWorker);
 14337  	/* All done */
 14338  	SXUNUSED(pKey); /* cc warning */
 14339  	return JX9_OK;
 14340  }
 14341  /*
 14342   * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
 14343   * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
 14344   *  Replace all occurrences of the search string with the replacement string.
 14345   * Parameters
 14346   *  If search and replace are arrays, then str_replace() takes a value from each
 14347   *  array and uses them to search and replace on subject. If replace has fewer values
 14348   *  than search, then an empty string is used for the rest of replacement values.
 14349   *  If search is an array and replace is a string, then this replacement string is used
 14350   *  for every value of search. The converse would not make sense, though.
 14351   *  If search or replace are arrays, their elements are processed first to last.
 14352   * $search
 14353   *  The value being searched for, otherwise known as the needle. An array may be used
 14354   *  to designate multiple needles.
 14355   * $replace
 14356   *  The replacement value that replaces found search values. An array may be used
 14357   *  to designate multiple replacements.
 14358   * $subject
 14359   *  The string or array being searched and replaced on, otherwise known as the haystack.
 14360   *  If subject is an array, then the search and replace is performed with every entry 
 14361   *  of subject, and the return value is an array as well.
 14362   * $count (Not used)
 14363   *  If passed, this will be set to the number of replacements performed.
 14364   * Return
 14365   * This function returns a string or an array with the replaced values.
 14366   */
 14367  static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14368  {
 14369  	SyString sTemp, *pSearch, *pReplace;
 14370  	ProcStringMatch xMatch;
 14371  	const char *zIn, *zFunc;
 14372  	str_replace_data sRep;
 14373  	SyBlob sWorker;
 14374  	SySet sReplace;
 14375  	SySet sSearch;
 14376  	int rep_str;
 14377  	int nByte;
 14378  	sxi32 rc;
 14379  	if( nArg < 3 ){
 14380  		/* Missing/Invalid arguments, return null */
 14381  		jx9_result_null(pCtx);
 14382  		return JX9_OK;
 14383  	}
 14384  	/* Initialize fields */
 14385  	SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
 14386  	SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
 14387  	SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
 14388  	SyZero(&sRep, sizeof(str_replace_data));
 14389  	sRep.pCtx = pCtx;
 14390  	sRep.pCollector = &sSearch;
 14391  	rep_str = 0;
 14392  	/* Extract the subject */
 14393  	zIn = jx9_value_to_string(apArg[2], &nByte);
 14394  	if( nByte < 1 ){
 14395  		/* Nothing to replace, return the empty string */
 14396  		jx9_result_string(pCtx, "", 0);
 14397  		return JX9_OK;
 14398  	}
 14399  	/* Copy the subject */
 14400  	SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
 14401  	/* Search string */
 14402  	if( jx9_value_is_json_array(apArg[0]) ){
 14403  		/* Collect search string */
 14404  		jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
 14405  	}else{
 14406  		/* Single pattern */
 14407  		zIn = jx9_value_to_string(apArg[0], &nByte);
 14408  		if( nByte < 1 ){
 14409  			/* Return the subject untouched since no search string is available */
 14410  			jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
 14411  			return JX9_OK;
 14412  		}
 14413  		SyStringInitFromBuf(&sTemp, zIn, nByte);
 14414  		/* Save for later processing */
 14415  		SySetPut(&sSearch, (const void *)&sTemp);
 14416  	}
 14417  	/* Replace string */
 14418  	if( jx9_value_is_json_array(apArg[1]) ){
 14419  		/* Collect replace string */
 14420  		sRep.pCollector = &sReplace;
 14421  		jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
 14422  	}else{
 14423  		/* Single needle */
 14424  		zIn = jx9_value_to_string(apArg[1], &nByte);
 14425  		rep_str = 1;
 14426  		SyStringInitFromBuf(&sTemp, zIn, nByte);
 14427  		/* Save for later processing */
 14428  		SySetPut(&sReplace, (const void *)&sTemp);
 14429  	}
 14430  	/* Reset loop cursors */
 14431  	SySetResetCursor(&sSearch);
 14432  	SySetResetCursor(&sReplace);
 14433  	pReplace = pSearch = 0; /* cc warning */
 14434  	SyStringInitFromBuf(&sTemp, "", 0);
 14435  	/* Extract function name */
 14436  	zFunc = jx9_function_name(pCtx);
 14437  	/* Set the default pattern match routine */
 14438  	xMatch = SyBlobSearch;
 14439  	if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) ==  0 ){
 14440  		/* Case insensitive pattern match */
 14441  		xMatch = iPatternMatch;
 14442  	}
 14443  	/* Start the replace process */
 14444  	while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
 14445  		sxu32 nCount, nOfft;
 14446  		if( pSearch->nByte <  1 ){
 14447  			/* Empty string, ignore */
 14448  			continue;
 14449  		}
 14450  		/* Extract the replace string */
 14451  		if( rep_str ){
 14452  			pReplace = (SyString *)SySetPeek(&sReplace);
 14453  		}else{
 14454  			if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
 14455  				/* Sepecial case when 'replace set' has fewer values than the search set.
 14456  				 * An empty string is used for the rest of replacement values
 14457  				 */
 14458  				pReplace = 0;
 14459  			}
 14460  		}
 14461  		if( pReplace == 0 ){
 14462  			/* Use an empty string instead */
 14463  			pReplace = &sTemp;
 14464  		}
 14465  		nOfft = nCount = 0;
 14466  		for(;;){
 14467  			if( nCount >= SyBlobLength(&sWorker) ){
 14468  				break;
 14469  			}
 14470  			/* Perform a pattern lookup */
 14471  			rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString, 
 14472  				pSearch->nByte, &nOfft);
 14473  			if( rc != SXRET_OK ){
 14474  				/* Pattern not found */
 14475  				break;
 14476  			}
 14477  			/* Perform the replace operation */
 14478  			StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
 14479  			/* Increment offset counter */
 14480  			nCount += nOfft + pReplace->nByte;
 14481  		}
 14482  	}
 14483  	/* All done, clean-up the mess left behind */
 14484  	jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
 14485  	SySetRelease(&sSearch);
 14486  	SySetRelease(&sReplace);
 14487  	SyBlobRelease(&sWorker);
 14488  	return JX9_OK;
 14489  }
 14490  /*
 14491   * string strtr(string $str, string $from, string $to)
 14492   * string strtr(string $str, array $replace_pairs)
 14493   *  Translate characters or replace substrings.
 14494   * Parameters
 14495   *  $str
 14496   *  The string being translated.
 14497   * $from
 14498   *  The string being translated to to.
 14499   * $to
 14500   *  The string replacing from.
 14501   * $replace_pairs
 14502   *  The replace_pairs parameter may be used instead of to and 
 14503   *  from, in which case it's an array in the form array('from' => 'to', ...).
 14504   * Return
 14505   *  The translated string.
 14506   *  If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
 14507   */
 14508  static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14509  {
 14510  	const char *zIn;
 14511  	int nLen;
 14512  	if( nArg < 1 ){
 14513  		/* Nothing to replace, return FALSE */
 14514  		jx9_result_bool(pCtx, 0);
 14515  		return JX9_OK;
 14516  	}
 14517  	zIn = jx9_value_to_string(apArg[0], &nLen);
 14518  	if( nLen < 1 || nArg < 2 ){
 14519  		/* Invalid arguments */
 14520  		jx9_result_string(pCtx, zIn, nLen);
 14521  		return JX9_OK;
 14522  	}
 14523  	if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
 14524  		str_replace_data sRepData;
 14525  		SyBlob sWorker;
 14526  		/* Initilaize the working buffer */
 14527  		SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
 14528  		/* Copy raw string */
 14529  		SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
 14530  		/* Init our replace data instance */
 14531  		sRepData.pWorker = &sWorker;
 14532  		sRepData.xMatch = SyBlobSearch;
 14533  		/* Iterate throw array entries and perform the replace operation.*/
 14534  		jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
 14535  		/* All done, return the result string */
 14536  		jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), 
 14537  			(int)SyBlobLength(&sWorker)); /* Will make it's own copy */
 14538  		/* Clean-up */
 14539  		SyBlobRelease(&sWorker);
 14540  	}else{
 14541  		int i, flen, tlen, c, iOfft;
 14542  		const char *zFrom, *zTo;
 14543  		if( nArg < 3 ){
 14544  			/* Nothing to replace */
 14545  			jx9_result_string(pCtx, zIn, nLen);
 14546  			return JX9_OK;
 14547  		}
 14548  		/* Extract given arguments */
 14549  		zFrom = jx9_value_to_string(apArg[1], &flen);
 14550  		zTo = jx9_value_to_string(apArg[2], &tlen);
 14551  		if( flen < 1 || tlen < 1 ){
 14552  			/* Nothing to replace */
 14553  			jx9_result_string(pCtx, zIn, nLen);
 14554  			return JX9_OK;
 14555  		}
 14556  		/* Start the replace process */
 14557  		for( i = 0 ; i < nLen ; ++i ){
 14558  			c = zIn[i];
 14559  			if( CheckMask(c, zFrom, flen, &iOfft) ){
 14560  				if ( iOfft < tlen ){
 14561  					c = zTo[iOfft];
 14562  				}
 14563  			}
 14564  			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 14565  			
 14566  		}
 14567  	}
 14568  	return JX9_OK;
 14569  }
 14570  /*
 14571   * Parse an INI string.
 14572   * According to wikipedia
 14573   *  The INI file format is an informal standard for configuration files for some platforms or software.
 14574   *  INI files are simple text files with a basic structure composed of "sections" and "properties".
 14575   *  Format
 14576  *    Properties
 14577  *     The basic element contained in an INI file is the property. Every property has a name and a value
 14578  *     delimited by an equals sign (=). The name appears to the left of the equals sign.
 14579  *     Example:
 14580  *      name=value
 14581  *    Sections
 14582  *     Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
 14583  *     in square brackets ([ and ]). All properties after the section declaration are associated with that section.
 14584  *     There is no explicit "end of section" delimiter; sections end at the next section declaration
 14585  *     or the end of the file. Sections may not be nested.
 14586  *     Example:
 14587  *      [section]
 14588  *   Comments
 14589  *    Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
 14590  * This function return an array holding parsed values on success.FALSE otherwise.
 14591  */
 14592  JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
 14593  {
 14594  	jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
 14595  	const char *zCur, *zEnd = &zIn[nByte];
 14596  	SyHashEntry *pEntry;
 14597  	SyString sEntry;
 14598  	SyHash sHash;
 14599  	int c;
 14600  	/* Create an empty array and worker variables */
 14601  	pArray = jx9_context_new_array(pCtx);
 14602  	pWorker = jx9_context_new_scalar(pCtx);
 14603  	pValue = jx9_context_new_scalar(pCtx);
 14604  	if( pArray == 0 || pWorker == 0 || pValue == 0){
 14605  		/* Out of memory */
 14606  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 14607  		/* Return FALSE */
 14608  		jx9_result_bool(pCtx, 0);
 14609  		return JX9_OK;
 14610  	}
 14611  	SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
 14612  	pCur = pArray;
 14613  	/* Start the parse process */
 14614  	for(;;){
 14615  		/* Ignore leading white spaces */
 14616  		while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
 14617  			zIn++;
 14618  		}
 14619  		if( zIn >= zEnd ){
 14620  			/* No more input to process */
 14621  			break;
 14622  		}
 14623  		if( zIn[0] == ';' || zIn[0] == '#' ){
 14624  			/* Comment til the end of line */
 14625  			zIn++;
 14626  			while(zIn < zEnd && zIn[0] != '\n' ){
 14627  				zIn++;
 14628  			}
 14629  			continue;
 14630  		}
 14631  		/* Reset the string cursor of the working variable */
 14632  		jx9_value_reset_string_cursor(pWorker);
 14633  		if( zIn[0] == '[' ){
 14634  			/* Section: Extract the section name */
 14635  			zIn++;
 14636  			zCur = zIn;
 14637  			while( zIn < zEnd && zIn[0] != ']' ){
 14638  				zIn++;
 14639  			}
 14640  			if( zIn > zCur && bProcessSection ){
 14641  				/* Save the section name */
 14642  				SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
 14643  				SyStringFullTrim(&sEntry);
 14644  				jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
 14645  				if( sEntry.nByte > 0 ){
 14646  					/* Associate an array with the section */
 14647  					pSection = jx9_context_new_array(pCtx);
 14648  					if( pSection ){
 14649  						jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
 14650  						pCur = pSection;
 14651  					}
 14652  				}
 14653  			}
 14654  			zIn++; /* Trailing square brackets ']' */
 14655  		}else{
 14656  			jx9_value *pOldCur;
 14657  			int is_array;
 14658  			int iLen;
 14659  			/* Properties */
 14660  			is_array = 0;
 14661  			zCur = zIn;
 14662  			iLen = 0; /* cc warning */
 14663  			pOldCur = pCur;
 14664  			while( zIn < zEnd && zIn[0] != '=' ){
 14665  				if( zIn[0] == '[' && !is_array ){
 14666  					/* Array */
 14667  					iLen = (int)(zIn-zCur);
 14668  					is_array = 1;
 14669  					if( iLen > 0 ){
 14670  						jx9_value *pvArr = 0; /* cc warning */
 14671  						/* Query the hashtable */
 14672  						SyStringInitFromBuf(&sEntry, zCur, iLen);
 14673  						SyStringFullTrim(&sEntry);
 14674  						pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
 14675  						if( pEntry ){
 14676  							pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
 14677  						}else{
 14678  							/* Create an empty array */
 14679  							pvArr = jx9_context_new_array(pCtx);
 14680  							if( pvArr ){
 14681  								/* Save the entry */
 14682  								SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
 14683  								/* Insert the entry */
 14684  								jx9_value_reset_string_cursor(pWorker);
 14685  								jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
 14686  								jx9_array_add_elem(pCur, pWorker, pvArr);
 14687  								jx9_value_reset_string_cursor(pWorker);
 14688  							}
 14689  						}
 14690  						if( pvArr ){
 14691  							pCur = pvArr;
 14692  						}
 14693  					}
 14694  					while ( zIn < zEnd && zIn[0] != ']' ){
 14695  						zIn++;
 14696  					}
 14697  				}
 14698  				zIn++;
 14699  			}
 14700  			if( !is_array ){
 14701  				iLen = (int)(zIn-zCur);
 14702  			}
 14703  			/* Trim the key */
 14704  			SyStringInitFromBuf(&sEntry, zCur, iLen);
 14705  			SyStringFullTrim(&sEntry);
 14706  			if( sEntry.nByte > 0 ){
 14707  				if( !is_array ){
 14708  					/* Save the key name */
 14709  					jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
 14710  				}
 14711  				/* extract key value */
 14712  				jx9_value_reset_string_cursor(pValue);
 14713  				zIn++; /* '=' */
 14714  				while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
 14715  					zIn++;
 14716  				}
 14717  				if( zIn < zEnd ){
 14718  					zCur = zIn;
 14719  					c = zIn[0];
 14720  					if( c == '"' || c == '\'' ){
 14721  						zIn++;
 14722  						/* Delimit the value */
 14723  						while( zIn < zEnd ){
 14724  							if ( zIn[0] == c && zIn[-1] != '\\' ){
 14725  								break;
 14726  							}
 14727  							zIn++;
 14728  						}
 14729  						if( zIn < zEnd ){
 14730  							zIn++;
 14731  						}
 14732  					}else{
 14733  						while( zIn < zEnd ){
 14734  							if( zIn[0] == '\n' ){
 14735  								if( zIn[-1] != '\\' ){
 14736  									break;
 14737  								}
 14738  							}else if( zIn[0] == ';' || zIn[0] == '#' ){
 14739  								/* Inline comments */
 14740  								break;
 14741  							}
 14742  							zIn++;
 14743  						}
 14744  					}
 14745  					/* Trim the value */
 14746  					SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
 14747  					SyStringFullTrim(&sEntry);
 14748  					if( c == '"' || c == '\'' ){
 14749  						SyStringTrimLeadingChar(&sEntry, c);
 14750  						SyStringTrimTrailingChar(&sEntry, c);
 14751  					}
 14752  					if( sEntry.nByte > 0 ){
 14753  						jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
 14754  					}
 14755  					/* Insert the key and it's value */
 14756  					jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
 14757  				}
 14758  			}else{
 14759  				while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
 14760  					zIn++;
 14761  				}
 14762  			}
 14763  			pCur = pOldCur;
 14764  		}
 14765  	}
 14766  	SyHashRelease(&sHash);
 14767  	/* Return the parse of the INI string */
 14768  	jx9_result_value(pCtx, pArray);
 14769  	return SXRET_OK;
 14770  }
 14771  /*
 14772   * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
 14773   *  Parse a configuration string.
 14774   * Parameters
 14775   *  $ini
 14776   *   The contents of the ini file being parsed.
 14777   *  $process_sections
 14778   *   By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
 14779   *   and settings included. The default for process_sections is FALSE.
 14780   *  $scanner_mode (Not used)
 14781   *   Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
 14782   *   then option values will not be parsed.
 14783   * Return
 14784   *  The settings are returned as an associative array on success, and FALSE on failure.
 14785   */
 14786  static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14787  {
 14788  	const char *zIni;
 14789  	int nByte;
 14790  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 14791  		/* Missing/Invalid arguments, return FALSE*/
 14792  		jx9_result_bool(pCtx, 0);
 14793  		return JX9_OK;
 14794  	}
 14795  	/* Extract the raw INI buffer */
 14796  	zIni = jx9_value_to_string(apArg[0], &nByte);
 14797  	/* Process the INI buffer*/
 14798  	jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
 14799  	return JX9_OK;
 14800  }
 14801  /*
 14802   * Ctype Functions.
 14803   * Authors:
 14804   *    Symisc Systems, devel@symisc.net.
 14805   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 14806   * Status:
 14807   *    Stable.
 14808   */
 14809  /*
 14810   * bool ctype_alnum(string $text)
 14811   *  Checks if all of the characters in the provided string, text, are alphanumeric.
 14812   * Parameters
 14813   *  $text
 14814   *   The tested string.
 14815   * Return
 14816   *   TRUE if every character in text is either a letter or a digit, FALSE otherwise.
 14817   */
 14818  static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14819  {
 14820  	const unsigned char *zIn, *zEnd;
 14821  	int nLen;
 14822  	if( nArg < 1 ){
 14823  		/* Missing arguments, return FALSE */
 14824  		jx9_result_bool(pCtx, 0);
 14825  		return JX9_OK;
 14826  	}
 14827  	/* Extract the target string */
 14828  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 14829  	zEnd = &zIn[nLen];
 14830  	if( nLen < 1 ){
 14831  		/* Empty string, return FALSE */
 14832  		jx9_result_bool(pCtx, 0);
 14833  		return JX9_OK;
 14834  	}
 14835  	/* Perform the requested operation */
 14836  	for(;;){
 14837  		if( zIn >= zEnd ){
 14838  			/* If we reach the end of the string, then the test succeeded. */
 14839  			jx9_result_bool(pCtx, 1);
 14840  			return JX9_OK;
 14841  		}
 14842  		if( !SyisAlphaNum(zIn[0]) ){
 14843  			break;
 14844  		}
 14845  		/* Point to the next character */
 14846  		zIn++;
 14847  	}
 14848  	/* The test failed, return FALSE */
 14849  	jx9_result_bool(pCtx, 0);
 14850  	return JX9_OK;
 14851  }
 14852  /*
 14853   * bool ctype_alpha(string $text)
 14854   *  Checks if all of the characters in the provided string, text, are alphabetic.
 14855   * Parameters
 14856   *  $text
 14857   *   The tested string.
 14858   * Return
 14859   *  TRUE if every character in text is a letter from the current locale, FALSE otherwise.
 14860   */
 14861  static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14862  {
 14863  	const unsigned char *zIn, *zEnd;
 14864  	int nLen;
 14865  	if( nArg < 1 ){
 14866  		/* Missing arguments, return FALSE */
 14867  		jx9_result_bool(pCtx, 0);
 14868  		return JX9_OK;
 14869  	}
 14870  	/* Extract the target string */
 14871  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 14872  	zEnd = &zIn[nLen];
 14873  	if( nLen < 1 ){
 14874  		/* Empty string, return FALSE */
 14875  		jx9_result_bool(pCtx, 0);
 14876  		return JX9_OK;
 14877  	}
 14878  	/* Perform the requested operation */
 14879  	for(;;){
 14880  		if( zIn >= zEnd ){
 14881  			/* If we reach the end of the string, then the test succeeded. */
 14882  			jx9_result_bool(pCtx, 1);
 14883  			return JX9_OK;
 14884  		}
 14885  		if( !SyisAlpha(zIn[0]) ){
 14886  			break;
 14887  		}
 14888  		/* Point to the next character */
 14889  		zIn++;
 14890  	}
 14891  	/* The test failed, return FALSE */
 14892  	jx9_result_bool(pCtx, 0);
 14893  	return JX9_OK;
 14894  }
 14895  /*
 14896   * bool ctype_cntrl(string $text)
 14897   *  Checks if all of the characters in the provided string, text, are control characters.
 14898   * Parameters
 14899   *  $text
 14900   *   The tested string.
 14901   * Return
 14902   *  TRUE if every character in text is a control characters, FALSE otherwise.
 14903   */
 14904  static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14905  {
 14906  	const unsigned char *zIn, *zEnd;
 14907  	int nLen;
 14908  	if( nArg < 1 ){
 14909  		/* Missing arguments, return FALSE */
 14910  		jx9_result_bool(pCtx, 0);
 14911  		return JX9_OK;
 14912  	}
 14913  	/* Extract the target string */
 14914  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 14915  	zEnd = &zIn[nLen];
 14916  	if( nLen < 1 ){
 14917  		/* Empty string, return FALSE */
 14918  		jx9_result_bool(pCtx, 0);
 14919  		return JX9_OK;
 14920  	}
 14921  	/* Perform the requested operation */
 14922  	for(;;){
 14923  		if( zIn >= zEnd ){
 14924  			/* If we reach the end of the string, then the test succeeded. */
 14925  			jx9_result_bool(pCtx, 1);
 14926  			return JX9_OK;
 14927  		}
 14928  		if( zIn[0] >= 0xc0 ){
 14929  			/* UTF-8 stream  */
 14930  			break;
 14931  		}
 14932  		if( !SyisCtrl(zIn[0]) ){
 14933  			break;
 14934  		}
 14935  		/* Point to the next character */
 14936  		zIn++;
 14937  	}
 14938  	/* The test failed, return FALSE */
 14939  	jx9_result_bool(pCtx, 0);
 14940  	return JX9_OK;
 14941  }
 14942  /*
 14943   * bool ctype_digit(string $text)
 14944   *  Checks if all of the characters in the provided string, text, are numerical.
 14945   * Parameters
 14946   *  $text
 14947   *   The tested string.
 14948   * Return
 14949   *  TRUE if every character in the string text is a decimal digit, FALSE otherwise.
 14950   */
 14951  static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
 14952  {
 14953  	const unsigned char *zIn, *zEnd;
 14954  	int nLen;
 14955  	if( nArg < 1 ){
 14956  		/* Missing arguments, return FALSE */
 14957  		jx9_result_bool(pCtx, 0);
 14958  		return JX9_OK;
 14959  	}
 14960  	/* Extract the target string */
 14961  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 14962  	zEnd = &zIn[nLen];
 14963  	if( nLen < 1 ){
 14964  		/* Empty string, return FALSE */
 14965  		jx9_result_bool(pCtx, 0);
 14966  		return JX9_OK;
 14967  	}
 14968  	/* Perform the requested operation */
 14969  	for(;;){
 14970  		if( zIn >= zEnd ){
 14971  			/* If we reach the end of the string, then the test succeeded. */
 14972  			jx9_result_bool(pCtx, 1);
 14973  			return JX9_OK;
 14974  		}
 14975  		if( zIn[0] >= 0xc0 ){
 14976  			/* UTF-8 stream  */
 14977  			break;
 14978  		}
 14979  		if( !SyisDigit(zIn[0]) ){
 14980  			break;
 14981  		}
 14982  		/* Point to the next character */
 14983  		zIn++;
 14984  	}
 14985  	/* The test failed, return FALSE */
 14986  	jx9_result_bool(pCtx, 0);
 14987  	return JX9_OK;
 14988  }
 14989  /*
 14990   * bool ctype_xdigit(string $text)
 14991   *  Check for character(s) representing a hexadecimal digit.
 14992   * Parameters
 14993   *  $text
 14994   *   The tested string.
 14995   * Return
 14996   *  Returns TRUE if every character in text is a hexadecimal 'digit', that is
 14997   * a decimal digit or a character from [A-Fa-f] , FALSE otherwise. 
 14998   */
 14999  static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15000  {
 15001  	const unsigned char *zIn, *zEnd;
 15002  	int nLen;
 15003  	if( nArg < 1 ){
 15004  		/* Missing arguments, return FALSE */
 15005  		jx9_result_bool(pCtx, 0);
 15006  		return JX9_OK;
 15007  	}
 15008  	/* Extract the target string */
 15009  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15010  	zEnd = &zIn[nLen];
 15011  	if( nLen < 1 ){
 15012  		/* Empty string, return FALSE */
 15013  		jx9_result_bool(pCtx, 0);
 15014  		return JX9_OK;
 15015  	}
 15016  	/* Perform the requested operation */
 15017  	for(;;){
 15018  		if( zIn >= zEnd ){
 15019  			/* If we reach the end of the string, then the test succeeded. */
 15020  			jx9_result_bool(pCtx, 1);
 15021  			return JX9_OK;
 15022  		}
 15023  		if( zIn[0] >= 0xc0 ){
 15024  			/* UTF-8 stream  */
 15025  			break;
 15026  		}
 15027  		if( !SyisHex(zIn[0]) ){
 15028  			break;
 15029  		}
 15030  		/* Point to the next character */
 15031  		zIn++;
 15032  	}
 15033  	/* The test failed, return FALSE */
 15034  	jx9_result_bool(pCtx, 0);
 15035  	return JX9_OK;
 15036  }
 15037  /*
 15038   * bool ctype_graph(string $text)
 15039   *  Checks if all of the characters in the provided string, text, creates visible output.
 15040   * Parameters
 15041   *  $text
 15042   *   The tested string.
 15043   * Return
 15044   *  Returns TRUE if every character in text is printable and actually creates visible output
 15045   * (no white space), FALSE otherwise. 
 15046   */
 15047  static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15048  {
 15049  	const unsigned char *zIn, *zEnd;
 15050  	int nLen;
 15051  	if( nArg < 1 ){
 15052  		/* Missing arguments, return FALSE */
 15053  		jx9_result_bool(pCtx, 0);
 15054  		return JX9_OK;
 15055  	}
 15056  	/* Extract the target string */
 15057  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15058  	zEnd = &zIn[nLen];
 15059  	if( nLen < 1 ){
 15060  		/* Empty string, return FALSE */
 15061  		jx9_result_bool(pCtx, 0);
 15062  		return JX9_OK;
 15063  	}
 15064  	/* Perform the requested operation */
 15065  	for(;;){
 15066  		if( zIn >= zEnd ){
 15067  			/* If we reach the end of the string, then the test succeeded. */
 15068  			jx9_result_bool(pCtx, 1);
 15069  			return JX9_OK;
 15070  		}
 15071  		if( zIn[0] >= 0xc0 ){
 15072  			/* UTF-8 stream  */
 15073  			break;
 15074  		}
 15075  		if( !SyisGraph(zIn[0]) ){
 15076  			break;
 15077  		}
 15078  		/* Point to the next character */
 15079  		zIn++;
 15080  	}
 15081  	/* The test failed, return FALSE */
 15082  	jx9_result_bool(pCtx, 0);
 15083  	return JX9_OK;
 15084  }
 15085  /*
 15086   * bool ctype_print(string $text)
 15087   *  Checks if all of the characters in the provided string, text, are printable.
 15088   * Parameters
 15089   *  $text
 15090   *   The tested string.
 15091   * Return
 15092   *  Returns TRUE if every character in text will actually create output (including blanks).
 15093   *  Returns FALSE if text contains control characters or characters that do not have any output
 15094   *  or control function at all. 
 15095   */
 15096  static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15097  {
 15098  	const unsigned char *zIn, *zEnd;
 15099  	int nLen;
 15100  	if( nArg < 1 ){
 15101  		/* Missing arguments, return FALSE */
 15102  		jx9_result_bool(pCtx, 0);
 15103  		return JX9_OK;
 15104  	}
 15105  	/* Extract the target string */
 15106  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15107  	zEnd = &zIn[nLen];
 15108  	if( nLen < 1 ){
 15109  		/* Empty string, return FALSE */
 15110  		jx9_result_bool(pCtx, 0);
 15111  		return JX9_OK;
 15112  	}
 15113  	/* Perform the requested operation */
 15114  	for(;;){
 15115  		if( zIn >= zEnd ){
 15116  			/* If we reach the end of the string, then the test succeeded. */
 15117  			jx9_result_bool(pCtx, 1);
 15118  			return JX9_OK;
 15119  		}
 15120  		if( zIn[0] >= 0xc0 ){
 15121  			/* UTF-8 stream  */
 15122  			break;
 15123  		}
 15124  		if( !SyisPrint(zIn[0]) ){
 15125  			break;
 15126  		}
 15127  		/* Point to the next character */
 15128  		zIn++;
 15129  	}
 15130  	/* The test failed, return FALSE */
 15131  	jx9_result_bool(pCtx, 0);
 15132  	return JX9_OK;
 15133  }
 15134  /*
 15135   * bool ctype_punct(string $text)
 15136   *  Checks if all of the characters in the provided string, text, are punctuation character.
 15137   * Parameters
 15138   *  $text
 15139   *   The tested string.
 15140   * Return
 15141   *  Returns TRUE if every character in text is printable, but neither letter
 15142   *  digit or blank, FALSE otherwise.
 15143   */
 15144  static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15145  {
 15146  	const unsigned char *zIn, *zEnd;
 15147  	int nLen;
 15148  	if( nArg < 1 ){
 15149  		/* Missing arguments, return FALSE */
 15150  		jx9_result_bool(pCtx, 0);
 15151  		return JX9_OK;
 15152  	}
 15153  	/* Extract the target string */
 15154  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15155  	zEnd = &zIn[nLen];
 15156  	if( nLen < 1 ){
 15157  		/* Empty string, return FALSE */
 15158  		jx9_result_bool(pCtx, 0);
 15159  		return JX9_OK;
 15160  	}
 15161  	/* Perform the requested operation */
 15162  	for(;;){
 15163  		if( zIn >= zEnd ){
 15164  			/* If we reach the end of the string, then the test succeeded. */
 15165  			jx9_result_bool(pCtx, 1);
 15166  			return JX9_OK;
 15167  		}
 15168  		if( zIn[0] >= 0xc0 ){
 15169  			/* UTF-8 stream  */
 15170  			break;
 15171  		}
 15172  		if( !SyisPunct(zIn[0]) ){
 15173  			break;
 15174  		}
 15175  		/* Point to the next character */
 15176  		zIn++;
 15177  	}
 15178  	/* The test failed, return FALSE */
 15179  	jx9_result_bool(pCtx, 0);
 15180  	return JX9_OK;
 15181  }
 15182  /*
 15183   * bool ctype_space(string $text)
 15184   *  Checks if all of the characters in the provided string, text, creates whitespace.
 15185   * Parameters
 15186   *  $text
 15187   *   The tested string.
 15188   * Return
 15189   *  Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
 15190   *  Besides the blank character this also includes tab, vertical tab, line feed, carriage return
 15191   *  and form feed characters. 
 15192   */
 15193  static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15194  {
 15195  	const unsigned char *zIn, *zEnd;
 15196  	int nLen;
 15197  	if( nArg < 1 ){
 15198  		/* Missing arguments, return FALSE */
 15199  		jx9_result_bool(pCtx, 0);
 15200  		return JX9_OK;
 15201  	}
 15202  	/* Extract the target string */
 15203  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15204  	zEnd = &zIn[nLen];
 15205  	if( nLen < 1 ){
 15206  		/* Empty string, return FALSE */
 15207  		jx9_result_bool(pCtx, 0);
 15208  		return JX9_OK;
 15209  	}
 15210  	/* Perform the requested operation */
 15211  	for(;;){
 15212  		if( zIn >= zEnd ){
 15213  			/* If we reach the end of the string, then the test succeeded. */
 15214  			jx9_result_bool(pCtx, 1);
 15215  			return JX9_OK;
 15216  		}
 15217  		if( zIn[0] >= 0xc0 ){
 15218  			/* UTF-8 stream  */
 15219  			break;
 15220  		}
 15221  		if( !SyisSpace(zIn[0]) ){
 15222  			break;
 15223  		}
 15224  		/* Point to the next character */
 15225  		zIn++;
 15226  	}
 15227  	/* The test failed, return FALSE */
 15228  	jx9_result_bool(pCtx, 0);
 15229  	return JX9_OK;
 15230  }
 15231  /*
 15232   * bool ctype_lower(string $text)
 15233   *  Checks if all of the characters in the provided string, text, are lowercase letters.
 15234   * Parameters
 15235   *  $text
 15236   *   The tested string.
 15237   * Return
 15238   *  Returns TRUE if every character in text is a lowercase letter in the current locale. 
 15239   */
 15240  static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15241  {
 15242  	const unsigned char *zIn, *zEnd;
 15243  	int nLen;
 15244  	if( nArg < 1 ){
 15245  		/* Missing arguments, return FALSE */
 15246  		jx9_result_bool(pCtx, 0);
 15247  		return JX9_OK;
 15248  	}
 15249  	/* Extract the target string */
 15250  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15251  	zEnd = &zIn[nLen];
 15252  	if( nLen < 1 ){
 15253  		/* Empty string, return FALSE */
 15254  		jx9_result_bool(pCtx, 0);
 15255  		return JX9_OK;
 15256  	}
 15257  	/* Perform the requested operation */
 15258  	for(;;){
 15259  		if( zIn >= zEnd ){
 15260  			/* If we reach the end of the string, then the test succeeded. */
 15261  			jx9_result_bool(pCtx, 1);
 15262  			return JX9_OK;
 15263  		}
 15264  		if( !SyisLower(zIn[0]) ){
 15265  			break;
 15266  		}
 15267  		/* Point to the next character */
 15268  		zIn++;
 15269  	}
 15270  	/* The test failed, return FALSE */
 15271  	jx9_result_bool(pCtx, 0);
 15272  	return JX9_OK;
 15273  }
 15274  /*
 15275   * bool ctype_upper(string $text)
 15276   *  Checks if all of the characters in the provided string, text, are uppercase letters.
 15277   * Parameters
 15278   *  $text
 15279   *   The tested string.
 15280   * Return
 15281   *  Returns TRUE if every character in text is a uppercase letter in the current locale. 
 15282   */
 15283  static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15284  {
 15285  	const unsigned char *zIn, *zEnd;
 15286  	int nLen;
 15287  	if( nArg < 1 ){
 15288  		/* Missing arguments, return FALSE */
 15289  		jx9_result_bool(pCtx, 0);
 15290  		return JX9_OK;
 15291  	}
 15292  	/* Extract the target string */
 15293  	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
 15294  	zEnd = &zIn[nLen];
 15295  	if( nLen < 1 ){
 15296  		/* Empty string, return FALSE */
 15297  		jx9_result_bool(pCtx, 0);
 15298  		return JX9_OK;
 15299  	}
 15300  	/* Perform the requested operation */
 15301  	for(;;){
 15302  		if( zIn >= zEnd ){
 15303  			/* If we reach the end of the string, then the test succeeded. */
 15304  			jx9_result_bool(pCtx, 1);
 15305  			return JX9_OK;
 15306  		}
 15307  		if( !SyisUpper(zIn[0]) ){
 15308  			break;
 15309  		}
 15310  		/* Point to the next character */
 15311  		zIn++;
 15312  	}
 15313  	/* The test failed, return FALSE */
 15314  	jx9_result_bool(pCtx, 0);
 15315  	return JX9_OK;
 15316  }
 15317  /*
 15318   * Date/Time functions
 15319   * Authors:
 15320   *    Symisc Systems, devel@symisc.net.
 15321   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 15322   * Status:
 15323   *    Devel.
 15324   */
 15325  #include <time.h>
 15326  #ifdef __WINNT__
 15327  /* GetSystemTime() */
 15328  #include <Windows.h> 
 15329  #ifdef _WIN32_WCE
 15330  /*
 15331  ** WindowsCE does not have a localtime() function.  So create a
 15332  ** substitute.
 15333  ** Taken from the SQLite3 source tree.
 15334  ** Status: Public domain
 15335  */
 15336  struct tm *__cdecl localtime(const time_t *t)
 15337  {
 15338    static struct tm y;
 15339    FILETIME uTm, lTm;
 15340    SYSTEMTIME pTm;
 15341    jx9_int64 t64;
 15342    t64 = *t;
 15343    t64 = (t64 + 11644473600)*10000000;
 15344    uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
 15345    uTm.dwHighDateTime= (DWORD)(t64 >> 32);
 15346    FileTimeToLocalFileTime(&uTm, &lTm);
 15347    FileTimeToSystemTime(&lTm, &pTm);
 15348    y.tm_year = pTm.wYear - 1900;
 15349    y.tm_mon = pTm.wMonth - 1;
 15350    y.tm_wday = pTm.wDayOfWeek;
 15351    y.tm_mday = pTm.wDay;
 15352    y.tm_hour = pTm.wHour;
 15353    y.tm_min = pTm.wMinute;
 15354    y.tm_sec = pTm.wSecond;
 15355    return &y;
 15356  }
 15357  #endif /*_WIN32_WCE */
 15358  #elif defined(__UNIXES__)
 15359  #include <sys/time.h>
 15360  #endif /* __WINNT__*/
 15361   /*
 15362    * int64 time(void)
 15363    *  Current Unix timestamp
 15364    * Parameters
 15365    *  None.
 15366    * Return
 15367    *  Returns the current time measured in the number of seconds
 15368    *  since the Unix Epoch (January 1 1970 00:00:00 GMT).
 15369    */
 15370  static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15371  {
 15372  	time_t tt;
 15373  	SXUNUSED(nArg); /* cc warning */
 15374  	SXUNUSED(apArg);
 15375  	/* Extract the current time */
 15376  	time(&tt);
 15377  	/* Return as 64-bit integer */
 15378  	jx9_result_int64(pCtx, (jx9_int64)tt);
 15379  	return  JX9_OK;
 15380  }
 15381  /*
 15382    * string/float microtime([ bool $get_as_float = false ])
 15383    *  microtime() returns the current Unix timestamp with microseconds.
 15384    * Parameters
 15385    *  $get_as_float
 15386    *   If used and set to TRUE, microtime() will return a float instead of a string
 15387    *   as described in the return values section below.
 15388    * Return
 15389    *  By default, microtime() returns a string in the form "msec sec", where sec 
 15390    *  is the current time measured in the number of seconds since the Unix 
 15391    *  epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
 15392    *  that have elapsed since sec expressed in seconds.
 15393    *  If get_as_float is set to TRUE, then microtime() returns a float, which represents
 15394    *  the current time in seconds since the Unix epoch accurate to the nearest microsecond. 
 15395    */
 15396  static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15397  {
 15398  	int bFloat = 0;
 15399  	sytime sTime;	
 15400  #if defined(__UNIXES__)
 15401  	struct timeval tv;
 15402  	gettimeofday(&tv, 0);
 15403  	sTime.tm_sec  = (long)tv.tv_sec;
 15404  	sTime.tm_usec = (long)tv.tv_usec;
 15405  #else
 15406  	time_t tt;
 15407  	time(&tt);
 15408  	sTime.tm_sec  = (long)tt;
 15409  	sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
 15410  #endif /* __UNIXES__ */
 15411  	if( nArg > 0 ){
 15412  		bFloat = jx9_value_to_bool(apArg[0]);
 15413  	}
 15414  	if( bFloat ){
 15415  		/* Return as float */
 15416  		jx9_result_double(pCtx, (double)sTime.tm_sec);
 15417  	}else{
 15418  		/* Return as string */
 15419  		jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
 15420  	}
 15421  	return JX9_OK;
 15422  }
 15423  /*
 15424   * array getdate ([ int $timestamp = time() ])
 15425   *  Get date/time information.
 15426   * Parameter
 15427   *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
 15428   *     that defaults to the current local time if a timestamp is not given.
 15429   *     In other words, it defaults to the value of time().
 15430   * Returns
 15431   *  Returns an associative array of information related to the timestamp.
 15432   *  Elements from the returned associative array are as follows: 
 15433   *   KEY                                                         VALUE
 15434   * ---------                                                    -------
 15435   * "seconds" 	Numeric representation of seconds 	            0 to 59
 15436   * "minutes" 	Numeric representation of minutes 	            0 to 59
 15437   * "hours" 	    Numeric representation of hours 	            0 to 23
 15438   * "mday" 	    Numeric representation of the day of the month 	1 to 31
 15439   * "wday" 	    Numeric representation of the day of the week 	0 (for Sunday) through 6 (for Saturday)
 15440   * "mon" 	    Numeric representation of a month 	            1 through 12
 15441   * "year" 	    A full numeric representation of a year,        4 digits 	Examples: 1999 or 2003
 15442   * "yday" 	    Numeric representation of the day of the year   0 through 365
 15443   * "weekday" 	A full textual representation of the day of the week 	Sunday through Saturday
 15444   * "month" 	    A full textual representation of a month, such as January or March 	January through December
 15445   * 0 	        Seconds since the Unix Epoch, similar to the values returned by time() and used by date(). 
 15446   * NOTE:
 15447   *   NULL is returned on failure.
 15448   */
 15449  static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15450  {
 15451  	jx9_value *pValue, *pArray;
 15452  	Sytm sTm;
 15453  	if( nArg < 1 ){
 15454  #ifdef __WINNT__
 15455  		SYSTEMTIME sOS;
 15456  		GetSystemTime(&sOS);
 15457  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 15458  #else
 15459  		struct tm *pTm;
 15460  		time_t t;
 15461  		time(&t);
 15462  		pTm = localtime(&t);
 15463  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 15464  #endif
 15465  	}else{
 15466  		/* Use the given timestamp */
 15467  		time_t t;
 15468  		struct tm *pTm;
 15469  #ifdef __WINNT__
 15470  #ifdef _MSC_VER
 15471  #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
 15472  #pragma warning(disable:4996) /* _CRT_SECURE...*/
 15473  #endif
 15474  #endif
 15475  #endif
 15476  		if( jx9_value_is_int(apArg[0]) ){
 15477  			t = (time_t)jx9_value_to_int64(apArg[0]);
 15478  			pTm = localtime(&t);
 15479  			if( pTm == 0 ){
 15480  				time(&t);
 15481  			}
 15482  		}else{
 15483  			time(&t);
 15484  		}
 15485  		pTm = localtime(&t);
 15486  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 15487  	}
 15488  	/* Element value */
 15489  	pValue = jx9_context_new_scalar(pCtx);
 15490  	if( pValue == 0 ){
 15491  		/* Return NULL */
 15492  		jx9_result_null(pCtx);
 15493  		return JX9_OK;
 15494  	}
 15495  	/* Create a new array */
 15496  	pArray = jx9_context_new_array(pCtx);
 15497  	if( pArray == 0 ){
 15498  		/* Return NULL */
 15499  		jx9_result_null(pCtx);
 15500  		return JX9_OK;
 15501  	}
 15502  	/* Fill the array */
 15503  	/* Seconds */
 15504  	jx9_value_int(pValue, sTm.tm_sec);
 15505  	jx9_array_add_strkey_elem(pArray, "seconds", pValue);
 15506  	/* Minutes */
 15507  	jx9_value_int(pValue, sTm.tm_min);
 15508  	jx9_array_add_strkey_elem(pArray, "minutes", pValue);
 15509  	/* Hours */
 15510  	jx9_value_int(pValue, sTm.tm_hour);
 15511  	jx9_array_add_strkey_elem(pArray, "hours", pValue);
 15512  	/* mday */
 15513  	jx9_value_int(pValue, sTm.tm_mday);
 15514  	jx9_array_add_strkey_elem(pArray, "mday", pValue);
 15515  	/* wday */
 15516  	jx9_value_int(pValue, sTm.tm_wday);
 15517  	jx9_array_add_strkey_elem(pArray, "wday", pValue);
 15518  	/* mon */
 15519  	jx9_value_int(pValue, sTm.tm_mon+1);
 15520  	jx9_array_add_strkey_elem(pArray, "mon", pValue);
 15521  	/* year */
 15522  	jx9_value_int(pValue, sTm.tm_year);
 15523  	jx9_array_add_strkey_elem(pArray, "year", pValue);
 15524  	/* yday */
 15525  	jx9_value_int(pValue, sTm.tm_yday);
 15526  	jx9_array_add_strkey_elem(pArray, "yday", pValue);
 15527  	/* Weekday */
 15528  	jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
 15529  	jx9_array_add_strkey_elem(pArray, "weekday", pValue);
 15530  	/* Month */
 15531  	jx9_value_reset_string_cursor(pValue);
 15532  	jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
 15533  	jx9_array_add_strkey_elem(pArray, "month", pValue);
 15534  	/* Seconds since the epoch */
 15535  	jx9_value_int64(pValue, (jx9_int64)time(0));
 15536  	jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
 15537  	/* Return the freshly created array */
 15538  	jx9_result_value(pCtx, pArray);
 15539  	return JX9_OK;
 15540  }
 15541  /*
 15542   * mixed gettimeofday([ bool $return_float = false ] )
 15543   *   Returns an associative array containing the data returned from the system call.
 15544   * Parameters
 15545   *  $return_float
 15546   *   When set to TRUE, a float instead of an array is returned.
 15547   * Return
 15548   *   By default an array is returned. If return_float is set, then
 15549   *   a float is returned. 
 15550   */
 15551  static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
 15552  {
 15553  	int bFloat = 0;
 15554  	sytime sTime;
 15555  #if defined(__UNIXES__)
 15556  	struct timeval tv;
 15557  	gettimeofday(&tv, 0);
 15558  	sTime.tm_sec  = (long)tv.tv_sec;
 15559  	sTime.tm_usec = (long)tv.tv_usec;
 15560  #else
 15561  	time_t tt;
 15562  	time(&tt);
 15563  	sTime.tm_sec  = (long)tt;
 15564  	sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
 15565  #endif /* __UNIXES__ */
 15566  	if( nArg > 0 ){
 15567  		bFloat = jx9_value_to_bool(apArg[0]);
 15568  	}
 15569  	if( bFloat ){
 15570  		/* Return as float */
 15571  		jx9_result_double(pCtx, (double)sTime.tm_sec);
 15572  	}else{
 15573  		/* Return an associative array */
 15574  		jx9_value *pValue, *pArray;
 15575  		/* Create a new array */
 15576  		pArray = jx9_context_new_array(pCtx);
 15577  		/* Element value */
 15578  		pValue = jx9_context_new_scalar(pCtx);
 15579  		if( pValue == 0 || pArray == 0 ){
 15580  			/* Return NULL */
 15581  			jx9_result_null(pCtx);
 15582  			return JX9_OK;
 15583  		}
 15584  		/* Fill the array */
 15585  		/* sec */
 15586  		jx9_value_int64(pValue, sTime.tm_sec);
 15587  		jx9_array_add_strkey_elem(pArray, "sec", pValue);
 15588  		/* usec */
 15589  		jx9_value_int64(pValue, sTime.tm_usec);
 15590  		jx9_array_add_strkey_elem(pArray, "usec", pValue);
 15591  		/* Return the array */
 15592  		jx9_result_value(pCtx, pArray);
 15593  	}
 15594  	return JX9_OK;
 15595  }
 15596  /* Check if the given year is leap or not */
 15597  #define IS_LEAP_YEAR(YEAR)	(YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
 15598  /* ISO-8601 numeric representation of the day of the week */
 15599  static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
 15600  /*
 15601   * Format a given date string.
 15602   * Supported format: (Taken from JX9 online docs)
 15603   * character 	Description
 15604   * d          Day of the month 
 15605   * D          A textual representation of a days
 15606   * j          Day of the month without leading zeros
 15607   * l          A full textual representation of the day of the week 	
 15608   * N          ISO-8601 numeric representation of the day of the week 
 15609   * w          Numeric representation of the day of the week
 15610   * z          The day of the year (starting from 0) 	
 15611   * F          A full textual representation of a month, such as January or March
 15612   * m          Numeric representation of a month, with leading zeros 	01 through 12
 15613   * M          A short textual representation of a month, three letters 	Jan through Dec
 15614   * n          Numeric representation of a month, without leading zeros 	1 through 12
 15615   * t          Number of days in the given month 	28 through 31
 15616   * L          Whether it's a leap year 	1 if it is a leap year, 0 otherwise.
 15617   * o          ISO-8601 year number. This has the same value as Y, except that if the ISO week number
 15618   *            (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
 15619   * Y          A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
 15620   * y          A two digit representation of a year 	Examples: 99 or 03
 15621   * a          Lowercase Ante meridiem and Post meridiem 	am or pm
 15622   * A          Uppercase Ante meridiem and Post meridiem 	AM or PM
 15623   * g          12-hour format of an hour without leading zeros 	1 through 12
 15624   * G          24-hour format of an hour without leading zeros 	0 through 23
 15625   * h          12-hour format of an hour with leading zeros 	01 through 12
 15626   * H          24-hour format of an hour with leading zeros 	00 through 23
 15627   * i          Minutes with leading zeros 	00 to 59
 15628   * s          Seconds, with leading zeros 	00 through 59
 15629   * u          Microseconds Example: 654321
 15630   * e          Timezone identifier 	Examples: UTC, GMT, Atlantic/Azores
 15631   * I          (capital i) Whether or not the date is in daylight saving time 	1 if Daylight Saving Time, 0 otherwise.
 15632   * r          RFC 2822 formatted date 	Example: Thu, 21 Dec 2000 16:01:07 +0200
 15633   * U          Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
 15634   * S          English ordinal suffix for the day of the month, 2 characters
 15635   * O          Difference to Greenwich time (GMT) in hours
 15636   * Z          Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
 15637   *            east of UTC is always positive.
 15638   * c         ISO 8601 date
 15639   */
 15640  static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
 15641  {
 15642  	const char *zEnd = &zIn[nLen];
 15643  	const char *zCur;
 15644  	/* Start the format process */
 15645  	for(;;){
 15646  		if( zIn >= zEnd ){
 15647  			/* No more input to process */
 15648  			break;
 15649  		}
 15650  		switch(zIn[0]){
 15651  		case 'd':
 15652  			/* Day of the month, 2 digits with leading zeros */
 15653  			jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
 15654  			break;
 15655  		case 'D':
 15656  			/*A textual representation of a day, three letters*/
 15657  			zCur = SyTimeGetDay(pTm->tm_wday);
 15658  			jx9_result_string(pCtx, zCur, 3);
 15659  			break;
 15660  		case 'j':
 15661  			/*	Day of the month without leading zeros */
 15662  			jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
 15663  			break;
 15664  		case 'l':
 15665  			/* A full textual representation of the day of the week */
 15666  			zCur = SyTimeGetDay(pTm->tm_wday);
 15667  			jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
 15668  			break;
 15669  		case 'N':{
 15670  			/* ISO-8601 numeric representation of the day of the week */
 15671  			jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
 15672  			break;
 15673  				 }
 15674  		case 'w':
 15675  			/*Numeric representation of the day of the week*/
 15676  			jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
 15677  			break;
 15678  		case 'z':
 15679  			/*The day of the year*/
 15680  			jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
 15681  			break;
 15682  		case 'F':
 15683  			/*A full textual representation of a month, such as January or March*/
 15684  			zCur = SyTimeGetMonth(pTm->tm_mon);
 15685  			jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
 15686  			break;
 15687  		case 'm':
 15688  			/*Numeric representation of a month, with leading zeros*/
 15689  			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
 15690  			break;
 15691  		case 'M':
 15692  			/*A short textual representation of a month, three letters*/
 15693  			zCur = SyTimeGetMonth(pTm->tm_mon);
 15694  			jx9_result_string(pCtx, zCur, 3);
 15695  			break;
 15696  		case 'n':
 15697  			/*Numeric representation of a month, without leading zeros*/
 15698  			jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
 15699  			break;
 15700  		case 't':{
 15701  			static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 15702  			int nDays = aMonDays[pTm->tm_mon % 12 ];
 15703  			if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
 15704  				nDays = 28;
 15705  			}
 15706  			/*Number of days in the given month*/
 15707  			jx9_result_string_format(pCtx, "%d", nDays);
 15708  			break;
 15709  				 }
 15710  		case 'L':{
 15711  			int isLeap = IS_LEAP_YEAR(pTm->tm_year);
 15712  			/* Whether it's a leap year */
 15713  			jx9_result_string_format(pCtx, "%d", isLeap);
 15714  			break;
 15715  				 }
 15716  		case 'o':
 15717  			/* ISO-8601 year number.*/
 15718  			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
 15719  			break;
 15720  		case 'Y':
 15721  			/*	A full numeric representation of a year, 4 digits */
 15722  			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
 15723  			break;
 15724  		case 'y':
 15725  			/*A two digit representation of a year*/
 15726  			jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
 15727  			break;
 15728  		case 'a':
 15729  			/*	Lowercase Ante meridiem and Post meridiem */
 15730  			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
 15731  			break;
 15732  		case 'A':
 15733  			/*	Uppercase Ante meridiem and Post meridiem */
 15734  			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
 15735  			break;
 15736  		case 'g':
 15737  			/*	12-hour format of an hour without leading zeros*/
 15738  			jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
 15739  			break;
 15740  		case 'G':
 15741  			/* 24-hour format of an hour without leading zeros */
 15742  			jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
 15743  			break;
 15744  		case 'h':
 15745  			/* 12-hour format of an hour with leading zeros */
 15746  			jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
 15747  			break;
 15748  		case 'H':
 15749  			/*	24-hour format of an hour with leading zeros */
 15750  			jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
 15751  			break;
 15752  		case 'i':
 15753  			/* 	Minutes with leading zeros */
 15754  			jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
 15755  			break;
 15756  		case 's':
 15757  			/* 	second with leading zeros */
 15758  			jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
 15759  			break;
 15760  		case 'u':
 15761  			/* 	Microseconds */
 15762  			jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
 15763  			break;
 15764  		case 'S':{
 15765  			/* English ordinal suffix for the day of the month, 2 characters */
 15766  			static const char zSuffix[] = "thstndrdthththththth";
 15767  			int v = pTm->tm_mday;
 15768  			jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
 15769  			break;
 15770  				 }
 15771  		case 'e':
 15772  			/* 	Timezone identifier */
 15773  			zCur = pTm->tm_zone;
 15774  			if( zCur == 0 ){
 15775  				/* Assume GMT */
 15776  				zCur = "GMT";
 15777  			}
 15778  			jx9_result_string(pCtx, zCur, -1);
 15779  			break;
 15780  		case 'I':
 15781  			/* Whether or not the date is in daylight saving time */
 15782  #ifdef __WINNT__
 15783  #ifdef _MSC_VER
 15784  #ifndef _WIN32_WCE
 15785  			_get_daylight(&pTm->tm_isdst);
 15786  #endif
 15787  #endif
 15788  #endif
 15789  			jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
 15790  			break;
 15791  		case 'r':
 15792  			/* RFC 2822 formatted date 	Example: Thu, 21 Dec 2000 16:01:07 */
 15793  			jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d", 
 15794  				SyTimeGetDay(pTm->tm_wday), 
 15795  				pTm->tm_mday, 
 15796  				SyTimeGetMonth(pTm->tm_mon), 
 15797  				pTm->tm_year, 
 15798  				pTm->tm_hour, 
 15799  				pTm->tm_min, 
 15800  				pTm->tm_sec
 15801  				);
 15802  			break;
 15803  		case 'U':{
 15804  			time_t tt;
 15805  			/* Seconds since the Unix Epoch */
 15806  			time(&tt);
 15807  			jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
 15808  			break;
 15809  				 }
 15810  		case 'O':
 15811  		case 'P':
 15812  			/* Difference to Greenwich time (GMT) in hours */
 15813  			jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
 15814  			break;
 15815  		case 'Z':
 15816  			/* Timezone offset in seconds. The offset for timezones west of UTC
 15817  			 * is always negative, and for those east of UTC is always positive.
 15818  			 */
 15819  			jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
 15820  			break;
 15821  		case 'c':
 15822  			/* 	ISO 8601 date */
 15823  			jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d", 
 15824  				pTm->tm_year, 
 15825  				pTm->tm_mon+1, 
 15826  				pTm->tm_mday, 
 15827  				pTm->tm_hour, 
 15828  				pTm->tm_min, 
 15829  				pTm->tm_sec, 
 15830  				pTm->tm_gmtoff
 15831  				);
 15832  			break;
 15833  		case '\\':
 15834  			zIn++;
 15835  			/* Expand verbatim */
 15836  			if( zIn < zEnd ){
 15837  				jx9_result_string(pCtx, zIn, (int)sizeof(char));
 15838  			}
 15839  			break;
 15840  		default:
 15841  			/* Unknown format specifer, expand verbatim */
 15842  			jx9_result_string(pCtx, zIn, (int)sizeof(char));
 15843  			break;
 15844  		}
 15845  		/* Point to the next character */
 15846  		zIn++;
 15847  	}
 15848  	return SXRET_OK;
 15849  }
 15850  /*
 15851   * JX9 implementation of the strftime() function.
 15852   * The following formats are supported:
 15853   * %a 	An abbreviated textual representation of the day
 15854   * %A 	A full textual representation of the day
 15855   * %d 	Two-digit day of the month (with leading zeros)
 15856   * %e 	Day of the month, with a space preceding single digits.
 15857   * %j 	Day of the year, 3 digits with leading zeros 
 15858   * %u 	ISO-8601 numeric representation of the day of the week 	1 (for Monday) though 7 (for Sunday)
 15859   * %w 	Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
 15860   * %U 	Week number of the given year, starting with the first Sunday as the first week
 15861   * %V 	ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
 15862   *   4 weekdays, with Monday being the start of the week.
 15863   * %W 	A numeric representation of the week of the year
 15864   * %b 	Abbreviated month name, based on the locale
 15865   * %B 	Full month name, based on the locale
 15866   * %h 	Abbreviated month name, based on the locale (an alias of %b)
 15867   * %m 	Two digit representation of the month
 15868   * %C 	Two digit representation of the century (year divided by 100, truncated to an integer)
 15869   * %g 	Two digit representation of the year going by ISO-8601:1988 standards (see %V)
 15870   * %G 	The full four-digit version of %g
 15871   * %y 	Two digit representation of the year
 15872   * %Y 	Four digit representation for the year
 15873   * %H 	Two digit representation of the hour in 24-hour format
 15874   * %I 	Two digit representation of the hour in 12-hour format
 15875   * %l (lower-case 'L') 	Hour in 12-hour format, with a space preceeding single digits
 15876   * %M 	Two digit representation of the minute
 15877   * %p 	UPPER-CASE 'AM' or 'PM' based on the given time
 15878   * %P 	lower-case 'am' or 'pm' based on the given time
 15879   * %r 	Same as "%I:%M:%S %p"
 15880   * %R 	Same as "%H:%M"
 15881   * %S 	Two digit representation of the second
 15882   * %T 	Same as "%H:%M:%S"
 15883   * %X 	Preferred time representation based on locale, without the date
 15884   * %z 	Either the time zone offset from UTC or the abbreviation
 15885   * %Z 	The time zone offset/abbreviation option NOT given by %z
 15886   * %c 	Preferred date and time stamp based on local
 15887   * %D 	Same as "%m/%d/%y"
 15888   * %F 	Same as "%Y-%m-%d"
 15889   * %s 	Unix Epoch Time timestamp (same as the time() function)
 15890   * %x 	Preferred date representation based on locale, without the time
 15891   * %n 	A newline character ("\n")
 15892   * %t 	A Tab character ("\t")
 15893   * %% 	A literal percentage character ("%")
 15894   */
 15895  static int jx9Strftime(
 15896  	jx9_context *pCtx,  /* Call context */
 15897  	const char *zIn,    /* Input string */
 15898  	int nLen,           /* Input length */
 15899  	Sytm *pTm           /* Parse of the given time */
 15900  	)
 15901  {
 15902  	const char *zCur, *zEnd = &zIn[nLen];
 15903  	int c;
 15904  	/* Start the format process */
 15905  	for(;;){
 15906  		zCur = zIn;
 15907  		while(zIn < zEnd && zIn[0] != '%' ){
 15908  			zIn++;
 15909  		}
 15910  		if( zIn > zCur ){
 15911  			/* Consume input verbatim */
 15912  			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
 15913  		}
 15914  		zIn++; /* Jump the percent sign */
 15915  		if( zIn >= zEnd ){
 15916  			/* No more input to process */
 15917  			break;
 15918  		}
 15919  		c = zIn[0];
 15920  		/* Act according to the current specifer */
 15921  		switch(c){
 15922  		case '%':
 15923  			/* A literal percentage character ("%") */
 15924  			jx9_result_string(pCtx, "%", (int)sizeof(char));
 15925  			break;
 15926  		case 't':
 15927  			/* A Tab character */
 15928  			jx9_result_string(pCtx, "\t", (int)sizeof(char));
 15929  			break;
 15930  		case 'n':
 15931  			/* A newline character */
 15932  			jx9_result_string(pCtx, "\n", (int)sizeof(char));
 15933  			break;
 15934  		case 'a':
 15935  			/* An abbreviated textual representation of the day */
 15936  			jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
 15937  			break;
 15938  		case 'A':
 15939  			/* A full textual representation of the day */
 15940  			jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
 15941  			break;
 15942  		case 'e':
 15943  			/* Day of the month, 2 digits with leading space for single digit*/
 15944  			jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
 15945  			break;
 15946  		case 'd':
 15947  			/* Two-digit day of the month (with leading zeros) */
 15948  			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
 15949  			break;
 15950  		case 'j':
 15951  			/*The day of the year, 3 digits with leading zeros*/
 15952  			jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
 15953  			break;
 15954  		case 'u':
 15955  			/* ISO-8601 numeric representation of the day of the week */
 15956  			jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
 15957  			break;
 15958  		case 'w':
 15959  			/* Numeric representation of the day of the week */
 15960  			jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
 15961  			break;
 15962  		case 'b':
 15963  		case 'h':
 15964  			/*A short textual representation of a month, three letters (Not based on locale)*/
 15965  			jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
 15966  			break;
 15967  		case 'B':
 15968  			/* Full month name (Not based on locale) */
 15969  			jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
 15970  			break;
 15971  		case 'm':
 15972  			/*Numeric representation of a month, with leading zeros*/
 15973  			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
 15974  			break;
 15975  		case 'C':
 15976  			/* Two digit representation of the century */
 15977  			jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
 15978  			break;
 15979  		case 'y':
 15980  		case 'g':
 15981  			/* Two digit representation of the year */
 15982  			jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
 15983  			break;
 15984  		case 'Y':
 15985  		case 'G':
 15986  			/* Four digit representation of the year */
 15987  			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
 15988  			break;
 15989  		case 'I':
 15990  			/* 12-hour format of an hour with leading zeros */
 15991  			jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
 15992  			break;
 15993  		case 'l':
 15994  			/* 12-hour format of an hour with leading space */
 15995  			jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
 15996  			break;
 15997  		case 'H':
 15998  			/* 24-hour format of an hour with leading zeros */
 15999  			jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
 16000  			break;
 16001  		case 'M':
 16002  			/* Minutes with leading zeros */
 16003  			jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
 16004  			break;
 16005  		case 'S':
 16006  			/* Seconds with leading zeros */
 16007  			jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
 16008  			break;
 16009  		case 'z':
 16010  		case 'Z':
 16011  			/* 	Timezone identifier */
 16012  			zCur = pTm->tm_zone;
 16013  			if( zCur == 0 ){
 16014  				/* Assume GMT */
 16015  				zCur = "GMT";
 16016  			}
 16017  			jx9_result_string(pCtx, zCur, -1);
 16018  			break;
 16019  		case 'T':
 16020  		case 'X':
 16021  			/* Same as "%H:%M:%S" */
 16022  			jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
 16023  			break;
 16024  		case 'R':
 16025  			/* Same as "%H:%M" */
 16026  			jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
 16027  			break;
 16028  		case 'P':
 16029  			/*	Lowercase Ante meridiem and Post meridiem */
 16030  			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
 16031  			break;
 16032  		case 'p':
 16033  			/*	Uppercase Ante meridiem and Post meridiem */
 16034  			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
 16035  			break;
 16036  		case 'r':
 16037  			/* Same as "%I:%M:%S %p" */
 16038  			jx9_result_string_format(pCtx, "%02d:%02d:%02d %s", 
 16039  				1+(pTm->tm_hour%12), 
 16040  				pTm->tm_min, 
 16041  				pTm->tm_sec, 
 16042  				pTm->tm_hour > 12 ? "PM" : "AM"
 16043  				);
 16044  			break;
 16045  		case 'D':
 16046  		case 'x':
 16047  			/* Same as "%m/%d/%y" */
 16048  			jx9_result_string_format(pCtx, "%02d/%02d/%02d", 
 16049  				pTm->tm_mon+1, 
 16050  				pTm->tm_mday, 
 16051  				pTm->tm_year%100
 16052  				);
 16053  			break;
 16054  		case 'F':
 16055  			/* Same as "%Y-%m-%d" */
 16056  			jx9_result_string_format(pCtx, "%d-%02d-%02d", 
 16057  				pTm->tm_year, 
 16058  				pTm->tm_mon+1, 
 16059  				pTm->tm_mday
 16060  				);
 16061  			break;
 16062  		case 'c':
 16063  			jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d", 
 16064  				pTm->tm_year, 
 16065  				pTm->tm_mon+1, 
 16066  				pTm->tm_mday, 
 16067  				pTm->tm_hour, 
 16068  				pTm->tm_min, 
 16069  				pTm->tm_sec
 16070  				);
 16071  			break;
 16072  		case 's':{
 16073  			time_t tt;
 16074  			/* Seconds since the Unix Epoch */
 16075  			time(&tt);
 16076  			jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
 16077  			break;
 16078  				 }
 16079  		default:
 16080  			/* unknown specifer, simply ignore*/
 16081  			break;
 16082  		}
 16083  		/* Advance the cursor */
 16084  		zIn++;
 16085  	}
 16086  	return SXRET_OK;
 16087  }
 16088  /*
 16089   * string date(string $format [, int $timestamp = time() ] )
 16090   *  Returns a string formatted according to the given format string using
 16091   *  the given integer timestamp or the current time if no timestamp is given.
 16092   *  In other words, timestamp is optional and defaults to the value of time(). 
 16093   * Parameters
 16094   *  $format
 16095   *   The format of the outputted date string (See code above)
 16096   * $timestamp
 16097   *   The optional timestamp parameter is an integer Unix timestamp
 16098   *   that defaults to the current local time if a timestamp is not given.
 16099   *   In other words, it defaults to the value of time(). 
 16100   * Return
 16101   *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
 16102   */
 16103  static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16104  {
 16105  	const char *zFormat;
 16106  	int nLen;
 16107  	Sytm sTm;
 16108  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 16109  		/* Missing/Invalid argument, return FALSE */
 16110  		jx9_result_bool(pCtx, 0);
 16111  		return JX9_OK;
 16112  	}
 16113  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 16114  	if( nLen < 1 ){
 16115  		/* Don't bother processing return the empty string */
 16116  		jx9_result_string(pCtx, "", 0);
 16117  	}
 16118  	if( nArg < 2 ){
 16119  #ifdef __WINNT__
 16120  		SYSTEMTIME sOS;
 16121  		GetSystemTime(&sOS);
 16122  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 16123  #else
 16124  		struct tm *pTm;
 16125  		time_t t;
 16126  		time(&t);
 16127  		pTm = localtime(&t);
 16128  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16129  #endif
 16130  	}else{
 16131  		/* Use the given timestamp */
 16132  		time_t t;
 16133  		struct tm *pTm;
 16134  		if( jx9_value_is_int(apArg[1]) ){
 16135  			t = (time_t)jx9_value_to_int64(apArg[1]);
 16136  			pTm = localtime(&t);
 16137  			if( pTm == 0 ){
 16138  				time(&t);
 16139  			}
 16140  		}else{
 16141  			time(&t);
 16142  		}
 16143  		pTm = localtime(&t);
 16144  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16145  	}
 16146  	/* Format the given string */
 16147  	DateFormat(pCtx, zFormat, nLen, &sTm);
 16148  	return JX9_OK;
 16149  }
 16150  /*
 16151   * string strftime(string $format [, int $timestamp = time() ] )
 16152   *  Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
 16153   * Parameters
 16154   *  $format
 16155   *   The format of the outputted date string (See code above)
 16156   * $timestamp
 16157   *   The optional timestamp parameter is an integer Unix timestamp
 16158   *   that defaults to the current local time if a timestamp is not given.
 16159   *   In other words, it defaults to the value of time(). 
 16160   * Return
 16161   * Returns a string formatted according format using the given timestamp
 16162   * or the current local time if no timestamp is given.
 16163   */
 16164  static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16165  {
 16166  	const char *zFormat;
 16167  	int nLen;
 16168  	Sytm sTm;
 16169  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 16170  		/* Missing/Invalid argument, return FALSE */
 16171  		jx9_result_bool(pCtx, 0);
 16172  		return JX9_OK;
 16173  	}
 16174  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 16175  	if( nLen < 1 ){
 16176  		/* Don't bother processing return FALSE */
 16177  		jx9_result_bool(pCtx, 0);
 16178  	}
 16179  	if( nArg < 2 ){
 16180  #ifdef __WINNT__
 16181  		SYSTEMTIME sOS;
 16182  		GetSystemTime(&sOS);
 16183  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 16184  #else
 16185  		struct tm *pTm;
 16186  		time_t t;
 16187  		time(&t);
 16188  		pTm = localtime(&t);
 16189  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16190  #endif
 16191  	}else{
 16192  		/* Use the given timestamp */
 16193  		time_t t;
 16194  		struct tm *pTm;
 16195  		if( jx9_value_is_int(apArg[1]) ){
 16196  			t = (time_t)jx9_value_to_int64(apArg[1]);
 16197  			pTm = localtime(&t);
 16198  			if( pTm == 0 ){
 16199  				time(&t);
 16200  			}
 16201  		}else{
 16202  			time(&t);
 16203  		}
 16204  		pTm = localtime(&t);
 16205  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16206  	}
 16207  	/* Format the given string */
 16208  	jx9Strftime(pCtx, zFormat, nLen, &sTm);
 16209  	if( jx9_context_result_buf_length(pCtx) < 1 ){
 16210  		/* Nothing was formatted, return FALSE */
 16211  		jx9_result_bool(pCtx, 0);
 16212  	}
 16213  	return JX9_OK;
 16214  }
 16215  /*
 16216   * string gmdate(string $format [, int $timestamp = time() ] )
 16217   *  Identical to the date() function except that the time returned
 16218   *  is Greenwich Mean Time (GMT).
 16219   * Parameters
 16220   *  $format
 16221   *  The format of the outputted date string (See code above)
 16222   *  $timestamp
 16223   *   The optional timestamp parameter is an integer Unix timestamp
 16224   *   that defaults to the current local time if a timestamp is not given.
 16225   *   In other words, it defaults to the value of time(). 
 16226   * Return
 16227   *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
 16228   */
 16229  static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16230  {
 16231  	const char *zFormat;
 16232  	int nLen;
 16233  	Sytm sTm;
 16234  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 16235  		/* Missing/Invalid argument, return FALSE */
 16236  		jx9_result_bool(pCtx, 0);
 16237  		return JX9_OK;
 16238  	}
 16239  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 16240  	if( nLen < 1 ){
 16241  		/* Don't bother processing return the empty string */
 16242  		jx9_result_string(pCtx, "", 0);
 16243  	}
 16244  	if( nArg < 2 ){
 16245  #ifdef __WINNT__
 16246  		SYSTEMTIME sOS;
 16247  		GetSystemTime(&sOS);
 16248  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 16249  #else
 16250  		struct tm *pTm;
 16251  		time_t t;
 16252  		time(&t);
 16253  		pTm = gmtime(&t);
 16254  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16255  #endif
 16256  	}else{
 16257  		/* Use the given timestamp */
 16258  		time_t t;
 16259  		struct tm *pTm;
 16260  		if( jx9_value_is_int(apArg[1]) ){
 16261  			t = (time_t)jx9_value_to_int64(apArg[1]);
 16262  			pTm = gmtime(&t);
 16263  			if( pTm == 0 ){
 16264  				time(&t);
 16265  			}
 16266  		}else{
 16267  			time(&t);
 16268  		}
 16269  		pTm = gmtime(&t);
 16270  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16271  	}
 16272  	/* Format the given string */
 16273  	DateFormat(pCtx, zFormat, nLen, &sTm);
 16274  	return JX9_OK;
 16275  }
 16276  /*
 16277   * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
 16278   *  Return the local time.
 16279   * Parameter
 16280   *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
 16281   *     that defaults to the current local time if a timestamp is not given.
 16282   *     In other words, it defaults to the value of time().
 16283   * $is_associative
 16284   *   If set to FALSE or not supplied then the array is returned as a regular, numerically
 16285   *   indexed array. If the argument is set to TRUE then localtime() returns an associative
 16286   *   array containing all the different elements of the structure returned by the C function
 16287   *   call to localtime. The names of the different keys of the associative array are as follows:
 16288   *      "tm_sec" - seconds, 0 to 59
 16289   *      "tm_min" - minutes, 0 to 59
 16290   *      "tm_hour" - hours, 0 to 23
 16291   *      "tm_mday" - day of the month, 1 to 31
 16292   *      "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
 16293   *      "tm_year" - years since 1900
 16294   *      "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
 16295   *      "tm_yday" - day of the year, 0 to 365
 16296   *      "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
 16297   * Returns
 16298   *  An associative array of information related to the timestamp.
 16299   */
 16300  static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16301  {
 16302  	jx9_value *pValue, *pArray;
 16303  	int isAssoc = 0;
 16304  	Sytm sTm;
 16305  	if( nArg < 1 ){
 16306  #ifdef __WINNT__
 16307  		SYSTEMTIME sOS;
 16308  		GetSystemTime(&sOS); /* TODO(chems): GMT not local */
 16309  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 16310  #else
 16311  		struct tm *pTm;
 16312  		time_t t;
 16313  		time(&t);
 16314  		pTm = localtime(&t);
 16315  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16316  #endif
 16317  	}else{
 16318  		/* Use the given timestamp */
 16319  		time_t t;
 16320  		struct tm *pTm;
 16321  		if( jx9_value_is_int(apArg[0]) ){
 16322  			t = (time_t)jx9_value_to_int64(apArg[0]);
 16323  			pTm = localtime(&t);
 16324  			if( pTm == 0 ){
 16325  				time(&t);
 16326  			}
 16327  		}else{
 16328  			time(&t);
 16329  		}
 16330  		pTm = localtime(&t);
 16331  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16332  	}
 16333  	/* Element value */
 16334  	pValue = jx9_context_new_scalar(pCtx);
 16335  	if( pValue == 0 ){
 16336  		/* Return NULL */
 16337  		jx9_result_null(pCtx);
 16338  		return JX9_OK;
 16339  	}
 16340  	/* Create a new array */
 16341  	pArray = jx9_context_new_array(pCtx);
 16342  	if( pArray == 0 ){
 16343  		/* Return NULL */
 16344  		jx9_result_null(pCtx);
 16345  		return JX9_OK;
 16346  	}
 16347  	if( nArg > 1 ){
 16348  		isAssoc = jx9_value_to_bool(apArg[1]); 
 16349  	}
 16350  	/* Fill the array */
 16351  	/* Seconds */
 16352  	jx9_value_int(pValue, sTm.tm_sec);
 16353  	if( isAssoc ){
 16354  		jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
 16355  	}else{
 16356  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16357  	}
 16358  	/* Minutes */
 16359  	jx9_value_int(pValue, sTm.tm_min);
 16360  	if( isAssoc ){
 16361  		jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
 16362  	}else{
 16363  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16364  	}
 16365  	/* Hours */
 16366  	jx9_value_int(pValue, sTm.tm_hour);
 16367  	if( isAssoc ){
 16368  		jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
 16369  	}else{
 16370  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16371  	}
 16372  	/* mday */
 16373  	jx9_value_int(pValue, sTm.tm_mday);
 16374  	if( isAssoc ){
 16375  		jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
 16376  	}else{
 16377  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16378  	}
 16379  	/* mon */
 16380  	jx9_value_int(pValue, sTm.tm_mon);
 16381  	if( isAssoc ){
 16382  		jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
 16383  	}else{
 16384  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16385  	}
 16386  	/* year since 1900 */
 16387  	jx9_value_int(pValue, sTm.tm_year-1900);
 16388  	if( isAssoc ){
 16389  		jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
 16390  	}else{
 16391  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16392  	}
 16393  	/* wday */
 16394  	jx9_value_int(pValue, sTm.tm_wday);
 16395  	if( isAssoc ){
 16396  		jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
 16397  	}else{
 16398  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16399  	}
 16400  	/* yday */
 16401  	jx9_value_int(pValue, sTm.tm_yday);
 16402  	if( isAssoc ){
 16403  		jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
 16404  	}else{
 16405  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16406  	}
 16407  	/* isdst */
 16408  #ifdef __WINNT__
 16409  #ifdef _MSC_VER
 16410  #ifndef _WIN32_WCE
 16411  			_get_daylight(&sTm.tm_isdst);
 16412  #endif
 16413  #endif
 16414  #endif
 16415  	jx9_value_int(pValue, sTm.tm_isdst);
 16416  	if( isAssoc ){
 16417  		jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
 16418  	}else{
 16419  		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
 16420  	}
 16421  	/* Return the array */
 16422  	jx9_result_value(pCtx, pArray);
 16423  	return JX9_OK;
 16424  }
 16425  /*
 16426   * int idate(string $format [, int $timestamp = time() ])
 16427   *  Returns a number formatted according to the given format string
 16428   *  using the given integer timestamp or the current local time if 
 16429   *  no timestamp is given. In other words, timestamp is optional and defaults
 16430   *  to the value of time().
 16431   *  Unlike the function date(), idate() accepts just one char in the format
 16432   *  parameter.
 16433   * $Parameters
 16434   *  Supported format
 16435   *   d 	Day of the month
 16436   *   h 	Hour (12 hour format)
 16437   *   H 	Hour (24 hour format)
 16438   *   i 	Minutes
 16439   *   I (uppercase i)1 if DST is activated, 0 otherwise
 16440   *   L (uppercase l) returns 1 for leap year, 0 otherwise
 16441   *   m 	Month number
 16442   *   s 	Seconds
 16443   *   t 	Days in current month
 16444   *   U 	Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
 16445   *   w 	Day of the week (0 on Sunday)
 16446   *   W 	ISO-8601 week number of year, weeks starting on Monday
 16447   *   y 	Year (1 or 2 digits - check note below)
 16448   *   Y 	Year (4 digits)
 16449   *   z 	Day of the year
 16450   *   Z 	Timezone offset in seconds
 16451   * $timestamp
 16452   *  The optional timestamp parameter is an integer Unix timestamp that defaults
 16453   *  to the current local time if a timestamp is not given. In other words, it defaults
 16454   *  to the value of time(). 
 16455   * Return
 16456   *  An integer. 
 16457   */
 16458  static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16459  {
 16460  	const char *zFormat;
 16461  	jx9_int64 iVal = 0;
 16462  	int nLen;
 16463  	Sytm sTm;
 16464  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 16465  		/* Missing/Invalid argument, return -1 */
 16466  		jx9_result_int(pCtx, -1);
 16467  		return JX9_OK;
 16468  	}
 16469  	zFormat = jx9_value_to_string(apArg[0], &nLen);
 16470  	if( nLen < 1 ){
 16471  		/* Don't bother processing return -1*/
 16472  		jx9_result_int(pCtx, -1);
 16473  	}
 16474  	if( nArg < 2 ){
 16475  #ifdef __WINNT__
 16476  		SYSTEMTIME sOS;
 16477  		GetSystemTime(&sOS);
 16478  		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 16479  #else
 16480  		struct tm *pTm;
 16481  		time_t t;
 16482  		time(&t);
 16483  		pTm = localtime(&t);
 16484  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16485  #endif
 16486  	}else{
 16487  		/* Use the given timestamp */
 16488  		time_t t;
 16489  		struct tm *pTm;
 16490  		if( jx9_value_is_int(apArg[1]) ){
 16491  			t = (time_t)jx9_value_to_int64(apArg[1]);
 16492  			pTm = localtime(&t);
 16493  			if( pTm == 0 ){
 16494  				time(&t);
 16495  			}
 16496  		}else{
 16497  			time(&t);
 16498  		}
 16499  		pTm = localtime(&t);
 16500  		STRUCT_TM_TO_SYTM(pTm, &sTm);
 16501  	}
 16502  	/* Perform the requested operation */
 16503  	switch(zFormat[0]){
 16504  	case 'd':
 16505  		/* Day of the month */
 16506  		iVal = sTm.tm_mday;
 16507  		break;
 16508  	case 'h':
 16509  		/*	Hour (12 hour format)*/
 16510  		iVal = 1 + (sTm.tm_hour % 12);
 16511  		break;
 16512  	case 'H':
 16513  		/* Hour (24 hour format)*/
 16514  		iVal = sTm.tm_hour;
 16515  		break;
 16516  	case 'i':
 16517  		/*Minutes*/
 16518  		iVal = sTm.tm_min;
 16519  		break;
 16520  	case 'I':
 16521  		/*	returns 1 if DST is activated, 0 otherwise */
 16522  #ifdef __WINNT__
 16523  #ifdef _MSC_VER
 16524  #ifndef _WIN32_WCE
 16525  			_get_daylight(&sTm.tm_isdst);
 16526  #endif
 16527  #endif
 16528  #endif
 16529  		iVal = sTm.tm_isdst;
 16530  		break;
 16531  	case 'L':
 16532  		/* 	returns 1 for leap year, 0 otherwise */
 16533  		iVal = IS_LEAP_YEAR(sTm.tm_year);
 16534  		break;
 16535  	case 'm':
 16536  		/* Month number*/
 16537  		iVal = sTm.tm_mon;
 16538  		break;
 16539  	case 's':
 16540  		/*Seconds*/
 16541  		iVal = sTm.tm_sec;
 16542  		break;
 16543  	case 't':{
 16544  		/*Days in current month*/
 16545  		static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 16546  		int nDays = aMonDays[sTm.tm_mon % 12 ];
 16547  		if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
 16548  			nDays = 28;
 16549  		}
 16550  		iVal = nDays;
 16551  		break;
 16552  			 }
 16553  	case 'U':
 16554  		/*Seconds since the Unix Epoch*/
 16555  		iVal = (jx9_int64)time(0);
 16556  		break;
 16557  	case 'w':
 16558  		/*	Day of the week (0 on Sunday) */
 16559  		iVal = sTm.tm_wday;
 16560  		break;
 16561  	case 'W': {
 16562  		/* ISO-8601 week number of year, weeks starting on Monday */
 16563  		static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
 16564  		iVal = aISO8601[sTm.tm_wday % 7 ];
 16565  		break;
 16566  			  }
 16567  	case 'y':
 16568  		/* Year (2 digits) */
 16569  		iVal = sTm.tm_year % 100;
 16570  		break;
 16571  	case 'Y':
 16572  		/* Year (4 digits) */
 16573  		iVal = sTm.tm_year;
 16574  		break;
 16575  	case 'z':
 16576  		/* Day of the year */
 16577  		iVal = sTm.tm_yday;
 16578  		break;
 16579  	case 'Z':
 16580  		/*Timezone offset in seconds*/
 16581  		iVal = sTm.tm_gmtoff;
 16582  		break;
 16583  	default:
 16584  		/* unknown format, throw a warning */
 16585  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
 16586  		break;
 16587  	}
 16588  	/* Return the time value */
 16589  	jx9_result_int64(pCtx, iVal);
 16590  	return JX9_OK;
 16591  }
 16592  /*
 16593   * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") 
 16594   *  [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
 16595   *  Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer 
 16596   *  containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
 16597   *  specified.
 16598   *  Arguments may be left out in order from right to left; any arguments thus omitted will be set to
 16599   *  the current value according to the local date and time.
 16600   * Parameters
 16601   * $hour
 16602   *  The number of the hour relevant to the start of the day determined by month, day and year.
 16603   *  Negative values reference the hour before midnight of the day in question. Values greater
 16604   *  than 23 reference the appropriate hour in the following day(s).
 16605   * $minute
 16606   *  The number of the minute relevant to the start of the hour. Negative values reference
 16607   *  the minute in the previous hour. Values greater than 59 reference the appropriate minute
 16608   *  in the following hour(s).
 16609   * $second
 16610   *  The number of seconds relevant to the start of the minute. Negative values reference 
 16611   *  the second in the previous minute. Values greater than 59 reference the appropriate 
 16612   * second in the following minute(s).
 16613   * $month
 16614   *  The number of the month relevant to the end of the previous year. Values 1 to 12 reference
 16615   *  the normal calendar months of the year in question. Values less than 1 (including negative values)
 16616   *  reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
 16617   * $day
 16618   *  The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31 
 16619   *  (depending upon the month) reference the normal days in the relevant month. Values less than 1
 16620   *  (including negative values) reference the days in the previous month, so 0 is the last day 
 16621   *  of the previous month, -1 is the day before that, etc. Values greater than the number of days
 16622   *  in the relevant month reference the appropriate day in the following month(s).
 16623   * $year
 16624   *  The number of the year, may be a two or four digit value, with values between 0-69 mapping
 16625   *  to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as 
 16626   *  most common today, the valid range for year is somewhere between 1901 and 2038.
 16627   * $is_dst
 16628   *  This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not, 
 16629   *  or -1 (the default) if it is unknown whether the time is within daylight savings time or not. 
 16630   * Return
 16631   *   mktime() returns the Unix timestamp of the arguments given. 
 16632   *   If the arguments are invalid, the function returns FALSE
 16633   */
 16634  static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16635  {
 16636  	const char *zFunction;
 16637  	jx9_int64 iVal = 0;
 16638  	struct tm *pTm;
 16639  	time_t t;
 16640  	/* Extract function name */
 16641  	zFunction = jx9_function_name(pCtx);
 16642  	/* Get the current time */
 16643  	time(&t);
 16644  	if( zFunction[0] == 'g' /* gmmktime */ ){
 16645  		pTm = gmtime(&t);
 16646  	}else{
 16647  		/* localtime */
 16648  		pTm = localtime(&t);
 16649  	}
 16650  	if( nArg > 0 ){
 16651  		int iVal;
 16652  		/* Hour */
 16653  		iVal = jx9_value_to_int(apArg[0]);
 16654  		pTm->tm_hour = iVal;
 16655  		if( nArg > 1 ){
 16656  			/* Minutes */
 16657  			iVal = jx9_value_to_int(apArg[1]);
 16658  			pTm->tm_min = iVal;
 16659  			if( nArg > 2 ){
 16660  				/* Seconds */
 16661  				iVal = jx9_value_to_int(apArg[2]);
 16662  				pTm->tm_sec = iVal;
 16663  				if( nArg > 3 ){
 16664  					/* Month */
 16665  					iVal = jx9_value_to_int(apArg[3]);
 16666  					pTm->tm_mon = iVal - 1;
 16667  					if( nArg > 4 ){
 16668  						/* mday */
 16669  						iVal = jx9_value_to_int(apArg[4]);
 16670  						pTm->tm_mday = iVal;
 16671  						if( nArg > 5 ){
 16672  							/* Year */
 16673  							iVal = jx9_value_to_int(apArg[5]);
 16674  							if( iVal > 1900 ){
 16675  								iVal -= 1900;
 16676  							}
 16677  							pTm->tm_year = iVal;
 16678  							if( nArg > 6 ){
 16679  								/* is_dst */
 16680  								iVal = jx9_value_to_bool(apArg[6]);
 16681  								pTm->tm_isdst = iVal;
 16682  							}
 16683  						}
 16684  					}
 16685  				}
 16686  			}
 16687  		}
 16688  	}
 16689  	/* Make the time */
 16690  	iVal = (jx9_int64)mktime(pTm);
 16691  	/* Return the timesatmp as a 64bit integer */
 16692  	jx9_result_int64(pCtx, iVal);
 16693  	return JX9_OK;
 16694  }
 16695  /*
 16696   * Section:
 16697   *    URL handling Functions.
 16698   * Authors:
 16699   *    Symisc Systems, devel@symisc.net.
 16700   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 16701   * Status:
 16702   *    Stable.
 16703   */
 16704  /*
 16705   * Output consumer callback for the standard Symisc routines.
 16706   * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
 16707   */
 16708  static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
 16709  {
 16710  	/* Store in the call context result buffer */
 16711  	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
 16712  	return SXRET_OK;
 16713  }
 16714  /*
 16715   * string base64_encode(string $data)
 16716   * string convert_uuencode(string $data)  
 16717   *  Encodes data with MIME base64
 16718   * Parameter
 16719   *  $data
 16720   *    Data to encode
 16721   * Return
 16722   *  Encoded data or FALSE on failure.
 16723   */
 16724  static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16725  {
 16726  	const char *zIn;
 16727  	int nLen;
 16728  	if( nArg < 1 ){
 16729  		/* Missing arguments, return FALSE */
 16730  		jx9_result_bool(pCtx, 0);
 16731  		return JX9_OK;
 16732  	}
 16733  	/* Extract the input string */
 16734  	zIn = jx9_value_to_string(apArg[0], &nLen);
 16735  	if( nLen < 1 ){
 16736  		/* Nothing to process, return FALSE */
 16737  		jx9_result_bool(pCtx, 0);
 16738  		return JX9_OK;
 16739  	}
 16740  	/* Perform the BASE64 encoding */
 16741  	SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
 16742  	return JX9_OK;
 16743  }
 16744  /*
 16745   * string base64_decode(string $data)
 16746   * string convert_uudecode(string $data)
 16747   *  Decodes data encoded with MIME base64
 16748   * Parameter
 16749   *  $data
 16750   *    Encoded data.
 16751   * Return
 16752   *  Returns the original data or FALSE on failure.
 16753   */
 16754  static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16755  {
 16756  	const char *zIn;
 16757  	int nLen;
 16758  	if( nArg < 1 ){
 16759  		/* Missing arguments, return FALSE */
 16760  		jx9_result_bool(pCtx, 0);
 16761  		return JX9_OK;
 16762  	}
 16763  	/* Extract the input string */
 16764  	zIn = jx9_value_to_string(apArg[0], &nLen);
 16765  	if( nLen < 1 ){
 16766  		/* Nothing to process, return FALSE */
 16767  		jx9_result_bool(pCtx, 0);
 16768  		return JX9_OK;
 16769  	}
 16770  	/* Perform the BASE64 decoding */
 16771  	SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
 16772  	return JX9_OK;
 16773  }
 16774  /*
 16775   * string urlencode(string $str)
 16776   *  URL encoding
 16777   * Parameter
 16778   *  $data
 16779   *   Input string.
 16780   * Return
 16781   *  Returns a string in which all non-alphanumeric characters except -_. have
 16782   *  been replaced with a percent (%) sign followed by two hex digits and spaces
 16783   *  encoded as plus (+) signs.
 16784   */
 16785  static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16786  {
 16787  	const char *zIn;
 16788  	int nLen;
 16789  	if( nArg < 1 ){
 16790  		/* Missing arguments, return FALSE */
 16791  		jx9_result_bool(pCtx, 0);
 16792  		return JX9_OK;
 16793  	}
 16794  	/* Extract the input string */
 16795  	zIn = jx9_value_to_string(apArg[0], &nLen);
 16796  	if( nLen < 1 ){
 16797  		/* Nothing to process, return FALSE */
 16798  		jx9_result_bool(pCtx, 0);
 16799  		return JX9_OK;
 16800  	}
 16801  	/* Perform the URL encoding */
 16802  	SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
 16803  	return JX9_OK;
 16804  }
 16805  /*
 16806   * string urldecode(string $str)
 16807   *  Decodes any %## encoding in the given string.
 16808   *  Plus symbols ('+') are decoded to a space character. 
 16809   * Parameter
 16810   *  $data
 16811   *    Input string.
 16812   * Return
 16813   *  Decoded URL or FALSE on failure.
 16814   */
 16815  static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 16816  {
 16817  	const char *zIn;
 16818  	int nLen;
 16819  	if( nArg < 1 ){
 16820  		/* Missing arguments, return FALSE */
 16821  		jx9_result_bool(pCtx, 0);
 16822  		return JX9_OK;
 16823  	}
 16824  	/* Extract the input string */
 16825  	zIn = jx9_value_to_string(apArg[0], &nLen);
 16826  	if( nLen < 1 ){
 16827  		/* Nothing to process, return FALSE */
 16828  		jx9_result_bool(pCtx, 0);
 16829  		return JX9_OK;
 16830  	}
 16831  	/* Perform the URL decoding */
 16832  	SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
 16833  	return JX9_OK;
 16834  }
 16835  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 16836  /* Table of the built-in functions */
 16837  static const jx9_builtin_func aBuiltInFunc[] = {
 16838  	   /* Variable handling functions */
 16839  	{ "is_bool"    , jx9Builtin_is_bool     }, 
 16840  	{ "is_float"   , jx9Builtin_is_float    }, 
 16841  	{ "is_real"    , jx9Builtin_is_float    }, 
 16842  	{ "is_double"  , jx9Builtin_is_float    }, 
 16843  	{ "is_int"     , jx9Builtin_is_int      }, 
 16844  	{ "is_integer" , jx9Builtin_is_int      }, 
 16845  	{ "is_long"    , jx9Builtin_is_int      }, 
 16846  	{ "is_string"  , jx9Builtin_is_string   }, 
 16847  	{ "is_null"    , jx9Builtin_is_null     }, 
 16848  	{ "is_numeric" , jx9Builtin_is_numeric  }, 
 16849  	{ "is_scalar"  , jx9Builtin_is_scalar   }, 
 16850  	{ "is_array"   , jx9Builtin_is_array    }, 
 16851  	{ "is_object"  , jx9Builtin_is_object   }, 
 16852  	{ "is_resource", jx9Builtin_is_resource }, 
 16853  	{ "douleval"   , jx9Builtin_floatval    }, 
 16854  	{ "floatval"   , jx9Builtin_floatval    }, 
 16855  	{ "intval"     , jx9Builtin_intval      }, 
 16856  	{ "strval"     , jx9Builtin_strval      }, 
 16857  	{ "empty"      , jx9Builtin_empty       }, 
 16858  #ifndef JX9_DISABLE_BUILTIN_FUNC
 16859  #ifdef JX9_ENABLE_MATH_FUNC
 16860  	   /* Math functions */
 16861  	{ "abs"  ,    jx9Builtin_abs          }, 
 16862  	{ "sqrt" ,    jx9Builtin_sqrt         }, 
 16863  	{ "exp"  ,    jx9Builtin_exp          }, 
 16864  	{ "floor",    jx9Builtin_floor        }, 
 16865  	{ "cos"  ,    jx9Builtin_cos          }, 
 16866  	{ "sin"  ,    jx9Builtin_sin          }, 
 16867  	{ "acos" ,    jx9Builtin_acos         }, 
 16868  	{ "asin" ,    jx9Builtin_asin         }, 
 16869  	{ "cosh" ,    jx9Builtin_cosh         }, 
 16870  	{ "sinh" ,    jx9Builtin_sinh         }, 
 16871  	{ "ceil" ,    jx9Builtin_ceil         }, 
 16872  	{ "tan"  ,    jx9Builtin_tan          }, 
 16873  	{ "tanh" ,    jx9Builtin_tanh         }, 
 16874  	{ "atan" ,    jx9Builtin_atan         }, 
 16875  	{ "atan2",    jx9Builtin_atan2        }, 
 16876  	{ "log"  ,    jx9Builtin_log          }, 
 16877  	{ "log10" ,   jx9Builtin_log10        }, 
 16878  	{ "pow"  ,    jx9Builtin_pow          }, 
 16879  	{ "pi",       jx9Builtin_pi           }, 
 16880  	{ "fmod",     jx9Builtin_fmod         }, 
 16881  	{ "hypot",    jx9Builtin_hypot        }, 
 16882  #endif /* JX9_ENABLE_MATH_FUNC */
 16883  	{ "round",    jx9Builtin_round        }, 
 16884  	{ "dechex", jx9Builtin_dechex         }, 
 16885  	{ "decoct", jx9Builtin_decoct         }, 
 16886  	{ "decbin", jx9Builtin_decbin         }, 
 16887  	{ "hexdec", jx9Builtin_hexdec         }, 
 16888  	{ "bindec", jx9Builtin_bindec         }, 
 16889  	{ "octdec", jx9Builtin_octdec         }, 
 16890  	{ "base_convert", jx9Builtin_base_convert }, 
 16891  	   /* String handling functions */
 16892  	{ "substr",          jx9Builtin_substr     }, 
 16893  	{ "substr_compare",  jx9Builtin_substr_compare }, 
 16894  	{ "substr_count",    jx9Builtin_substr_count }, 
 16895  	{ "chunk_split",     jx9Builtin_chunk_split}, 
 16896  	{ "htmlspecialchars", jx9Builtin_htmlspecialchars }, 
 16897  	{ "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode }, 
 16898  	{ "get_html_translation_table", jx9Builtin_get_html_translation_table }, 
 16899  	{ "htmlentities", jx9Builtin_htmlentities}, 
 16900  	{ "html_entity_decode", jx9Builtin_html_entity_decode}, 
 16901  	{ "strlen"     , jx9Builtin_strlen     }, 
 16902  	{ "strcmp"     , jx9Builtin_strcmp     }, 
 16903  	{ "strcoll"    , jx9Builtin_strcmp     }, 
 16904  	{ "strncmp"    , jx9Builtin_strncmp    }, 
 16905  	{ "strcasecmp" , jx9Builtin_strcasecmp }, 
 16906  	{ "strncasecmp", jx9Builtin_strncasecmp}, 
 16907  	{ "implode"    , jx9Builtin_implode    }, 
 16908  	{ "join"       , jx9Builtin_implode    }, 
 16909  	{ "implode_recursive" , jx9Builtin_implode_recursive }, 
 16910  	{ "join_recursive"    , jx9Builtin_implode_recursive }, 
 16911  	{ "explode"     , jx9Builtin_explode    }, 
 16912  	{ "trim"        , jx9Builtin_trim       }, 
 16913  	{ "rtrim"       , jx9Builtin_rtrim      }, 
 16914  	{ "chop"        , jx9Builtin_rtrim      }, 
 16915  	{ "ltrim"       , jx9Builtin_ltrim      }, 
 16916  	{ "strtolower",   jx9Builtin_strtolower }, 
 16917  	{ "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
 16918  	{ "strtoupper",   jx9Builtin_strtoupper }, 
 16919  	{ "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
 16920  	{ "ord",          jx9Builtin_ord        }, 
 16921  	{ "chr",          jx9Builtin_chr        }, 
 16922  	{ "bin2hex",      jx9Builtin_bin2hex    }, 
 16923  	{ "strstr",       jx9Builtin_strstr     }, 
 16924  	{ "stristr",      jx9Builtin_stristr    }, 
 16925  	{ "strchr",       jx9Builtin_strstr     }, 
 16926  	{ "strpos",       jx9Builtin_strpos     }, 
 16927  	{ "stripos",      jx9Builtin_stripos    }, 
 16928  	{ "strrpos",      jx9Builtin_strrpos    }, 
 16929  	{ "strripos",     jx9Builtin_strripos   }, 
 16930  	{ "strrchr",      jx9Builtin_strrchr    }, 
 16931  	{ "strrev",       jx9Builtin_strrev     }, 
 16932  	{ "str_repeat",   jx9Builtin_str_repeat }, 
 16933  	{ "nl2br",        jx9Builtin_nl2br      }, 
 16934  	{ "sprintf",      jx9Builtin_sprintf    }, 
 16935  	{ "printf",       jx9Builtin_printf     }, 
 16936  	{ "vprintf",      jx9Builtin_vprintf    }, 
 16937  	{ "vsprintf",     jx9Builtin_vsprintf   }, 
 16938  	{ "size_format",  jx9Builtin_size_format}, 
 16939  #if !defined(JX9_DISABLE_HASH_FUNC)
 16940  	{ "md5",          jx9Builtin_md5       }, 
 16941  	{ "sha1",         jx9Builtin_sha1      }, 
 16942  	{ "crc32",        jx9Builtin_crc32     }, 
 16943  #endif /* JX9_DISABLE_HASH_FUNC */
 16944  	{ "str_getcsv",   jx9Builtin_str_getcsv }, 
 16945  	{ "strip_tags",   jx9Builtin_strip_tags }, 
 16946  	{ "str_split",    jx9Builtin_str_split  }, 
 16947  	{ "strspn",       jx9Builtin_strspn     }, 
 16948  	{ "strcspn",      jx9Builtin_strcspn    }, 
 16949  	{ "strpbrk",      jx9Builtin_strpbrk    }, 
 16950  	{ "soundex",      jx9Builtin_soundex    }, 
 16951  	{ "wordwrap",     jx9Builtin_wordwrap   }, 
 16952  	{ "strtok",       jx9Builtin_strtok     }, 
 16953  	{ "str_pad",      jx9Builtin_str_pad    }, 
 16954  	{ "str_replace",  jx9Builtin_str_replace}, 
 16955  	{ "str_ireplace", jx9Builtin_str_replace}, 
 16956  	{ "strtr",        jx9Builtin_strtr      }, 
 16957  	{ "parse_ini_string", jx9Builtin_parse_ini_string}, 
 16958  	         /* Ctype functions */
 16959  	{ "ctype_alnum", jx9Builtin_ctype_alnum }, 
 16960  	{ "ctype_alpha", jx9Builtin_ctype_alpha }, 
 16961  	{ "ctype_cntrl", jx9Builtin_ctype_cntrl }, 
 16962  	{ "ctype_digit", jx9Builtin_ctype_digit }, 
 16963  	{ "ctype_xdigit", jx9Builtin_ctype_xdigit}, 
 16964  	{ "ctype_graph", jx9Builtin_ctype_graph }, 
 16965  	{ "ctype_print", jx9Builtin_ctype_print }, 
 16966  	{ "ctype_punct", jx9Builtin_ctype_punct }, 
 16967  	{ "ctype_space", jx9Builtin_ctype_space }, 
 16968  	{ "ctype_lower", jx9Builtin_ctype_lower }, 
 16969  	{ "ctype_upper", jx9Builtin_ctype_upper }, 
 16970  	         /* Time functions */
 16971  	{ "time"    ,    jx9Builtin_time         }, 
 16972  	{ "microtime",   jx9Builtin_microtime    }, 
 16973  	{ "getdate" ,    jx9Builtin_getdate      }, 
 16974  	{ "gettimeofday", jx9Builtin_gettimeofday }, 
 16975  	{ "date",        jx9Builtin_date         }, 
 16976  	{ "strftime",    jx9Builtin_strftime     }, 
 16977  	{ "idate",       jx9Builtin_idate        }, 
 16978  	{ "gmdate",      jx9Builtin_gmdate       }, 
 16979  	{ "localtime",   jx9Builtin_localtime    }, 
 16980  	{ "mktime",      jx9Builtin_mktime       }, 
 16981  	{ "gmmktime",    jx9Builtin_mktime       }, 
 16982  	        /* URL functions */
 16983  	{ "base64_encode", jx9Builtin_base64_encode }, 
 16984  	{ "base64_decode", jx9Builtin_base64_decode }, 
 16985  	{ "convert_uuencode", jx9Builtin_base64_encode }, 
 16986  	{ "convert_uudecode", jx9Builtin_base64_decode }, 
 16987  	{ "urlencode",    jx9Builtin_urlencode }, 
 16988  	{ "urldecode",    jx9Builtin_urldecode }, 
 16989  	{ "rawurlencode", jx9Builtin_urlencode }, 
 16990  	{ "rawurldecode", jx9Builtin_urldecode }, 
 16991  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 16992  };
 16993  /*
 16994   * Register the built-in functions defined above, the array functions 
 16995   * defined in hashmap.c and the IO functions defined in vfs.c.
 16996   */
 16997  JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
 16998  {
 16999  	sxu32 n;
 17000  	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
 17001  		jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
 17002  	}
 17003  	/* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
 17004  	jx9RegisterHashmapFunctions(&(*pVm));
 17005  	/* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
 17006  	jx9RegisterIORoutine(&(*pVm));
 17007  }
 17008  
 17009  /*
 17010   * ----------------------------------------------------------
 17011   * File: jx9_compile.c
 17012   * MD5: 562e73eb7214f890e71713c6b97a7863
 17013   * ----------------------------------------------------------
 17014   */
 17015  /*
 17016   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 17017   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 17018   * Version 1.7.2
 17019   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 17020   * please contact Symisc Systems via:
 17021   *       legal@symisc.net
 17022   *       licensing@symisc.net
 17023   *       contact@symisc.net
 17024   * or visit:
 17025   *      http://jx9.symisc.net/
 17026   */
 17027   /* $SymiscID: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
 17028  #ifndef JX9_AMALGAMATION
 17029  #include "jx9Int.h"
 17030  #endif
 17031  /*
 17032   * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
 17033   * That is, routines defined in this file takes a stream of tokens and output
 17034   * JX9 bytecode instructions.
 17035   */
 17036  /* Forward declaration */
 17037  typedef struct LangConstruct LangConstruct;
 17038  typedef struct JumpFixup     JumpFixup;
 17039  /* Block [i.e: set of statements] control flags */
 17040  #define GEN_BLOCK_LOOP        0x001    /* Loop block [i.e: for, while, ...] */
 17041  #define GEN_BLOCK_PROTECTED   0x002    /* Protected block */
 17042  #define GEN_BLOCK_COND        0x004    /* Conditional block [i.e: if(condition){} ]*/
 17043  #define GEN_BLOCK_FUNC        0x008    /* Function body */
 17044  #define GEN_BLOCK_GLOBAL      0x010    /* Global block (always set)*/
 17045  #define GEN_BLOC_NESTED_FUNC  0x020    /* Nested function body */
 17046  #define GEN_BLOCK_EXPR        0x040    /* Expression */
 17047  #define GEN_BLOCK_STD         0x080    /* Standard block */
 17048  #define GEN_BLOCK_SWITCH      0x100    /* Switch statement */
 17049  /*
 17050   * Compilation of some JX9 constructs such as if, for, while, the logical or
 17051   * (||) and logical and (&&) operators in expressions requires the
 17052   * generation of forward jumps.
 17053   * Since the destination PC target of these jumps isn't known when the jumps
 17054   * are emitted, we record each forward jump in an instance of the following
 17055   * structure. Those jumps are fixed later when the jump destination is resolved.
 17056   */
 17057  struct JumpFixup
 17058  {
 17059  	sxi32 nJumpType;     /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
 17060  	sxu32 nInstrIdx;     /* Instruction index to fix later when the jump destination is resolved. */
 17061  };
 17062  /*
 17063   * Each language construct is represented by an instance
 17064   * of the following structure.
 17065   */
 17066  struct LangConstruct
 17067  {
 17068  	sxu32 nID;                     /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
 17069  	ProcLangConstruct xConstruct;  /* C function implementing the language construct */
 17070  };
 17071  /* Compilation flags */
 17072  #define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
 17073  /* Token stream synchronization macros */
 17074  #define SWAP_TOKEN_STREAM(GEN, START, END)\
 17075  	pTmp  = GEN->pEnd;\
 17076  	pGen->pIn  = START;\
 17077  	pGen->pEnd = END
 17078  #define UPDATE_TOKEN_STREAM(GEN)\
 17079  	if( GEN->pIn < pTmp ){\
 17080  	    GEN->pIn++;\
 17081  	}\
 17082  	GEN->pEnd = pTmp
 17083  #define SWAP_DELIMITER(GEN, START, END)\
 17084  	pTmpIn  = GEN->pIn;\
 17085  	pTmpEnd = GEN->pEnd;\
 17086  	GEN->pIn = START;\
 17087  	GEN->pEnd = END
 17088  #define RE_SWAP_DELIMITER(GEN)\
 17089  	GEN->pIn  = pTmpIn;\
 17090  	GEN->pEnd = pTmpEnd
 17091  /* Flags related to expression compilation */
 17092  #define EXPR_FLAG_LOAD_IDX_STORE    0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
 17093  #define EXPR_FLAG_RDONLY_LOAD       0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
 17094  #define EXPR_FLAG_COMMA_STATEMENT   0x004 /* Treat comma expression as a single statement (used by object attributes) */
 17095  /* Forward declaration */
 17096  static sxi32 jx9CompileExpr(
 17097  	jx9_gen_state *pGen, /* Code generator state */
 17098  	sxi32 iFlags,        /* Control flags */
 17099  	sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
 17100  	);
 17101  
 17102  /*
 17103   * Recover from a compile-time error. In other words synchronize
 17104   * the token stream cursor with the first semi-colon seen.
 17105   */
 17106  static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
 17107  {
 17108  	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
 17109  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
 17110  		pGen->pIn++;
 17111  	}
 17112  	return SXRET_OK;
 17113  }
 17114  /*
 17115   * Check if the given identifier name is reserved or not.
 17116   * Return TRUE if reserved.FALSE otherwise.
 17117   */
 17118  static int GenStateIsReservedID(SyString *pName)
 17119  {
 17120  	if( pName->nByte == sizeof("null") - 1 ){
 17121  		if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
 17122  			return TRUE;
 17123  		}else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
 17124  			return TRUE;
 17125  		}
 17126  	}else if( pName->nByte == sizeof("false") - 1 ){
 17127  		if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
 17128  			return TRUE;
 17129  		}
 17130  	}
 17131  	/* Not a reserved constant */
 17132  	return FALSE;
 17133  }
 17134  /*
 17135   * Check if a given token value is installed in the literal table.
 17136   */
 17137  static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
 17138  {
 17139  	SyHashEntry *pEntry;
 17140  	pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
 17141  	if( pEntry == 0 ){
 17142  		return SXERR_NOTFOUND;
 17143  	}
 17144  	*pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
 17145  	return SXRET_OK;
 17146  }
 17147  /*
 17148   * Install a given constant index in the literal table.
 17149   * In order to be installed, the jx9_value must be of type string.
 17150   */
 17151  static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
 17152  {
 17153  	if( SyBlobLength(&pObj->sBlob) > 0 ){
 17154  		SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
 17155  	}
 17156  	return SXRET_OK;
 17157  }
 17158  /*
 17159   * Generate a fatal error.
 17160   */
 17161  static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
 17162  {
 17163  	jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
 17164  	/* Abort compilation immediately */
 17165  	return SXERR_ABORT;
 17166  }
 17167  /*
 17168   * Fetch a block that correspond to the given criteria from the stack of
 17169   * compiled blocks.
 17170   * Return a pointer to that block on success. NULL otherwise.
 17171   */
 17172  static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
 17173  {
 17174  	GenBlock *pBlock = pCurrent;
 17175  	for(;;){
 17176  		if( pBlock->iFlags & iBlockType ){
 17177  			iCount--; /* Decrement nesting level */
 17178  			if( iCount < 1 ){
 17179  				/* Block meet with the desired criteria */
 17180  				return pBlock;
 17181  			}
 17182  		}
 17183  		/* Point to the upper block */
 17184  		pBlock = pBlock->pParent;
 17185  		if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
 17186  			/* Forbidden */
 17187  			break;
 17188  		}
 17189  	}
 17190  	/* No such block */
 17191  	return 0;
 17192  }
 17193  /*
 17194   * Initialize a freshly allocated block instance.
 17195   */
 17196  static void GenStateInitBlock(
 17197  	jx9_gen_state *pGen, /* Code generator state */
 17198  	GenBlock *pBlock,    /* Target block */
 17199  	sxi32 iType,         /* Block type [i.e: loop, conditional, function body, etc.]*/
 17200  	sxu32 nFirstInstr,   /* First instruction to compile */
 17201  	void *pUserData      /* Upper layer private data */
 17202  	)
 17203  {
 17204  	/* Initialize block fields */
 17205  	pBlock->nFirstInstr = nFirstInstr;
 17206  	pBlock->pUserData   = pUserData;
 17207  	pBlock->pGen        = pGen;
 17208  	pBlock->iFlags      = iType;
 17209  	pBlock->pParent     = 0;
 17210  	pBlock->bPostContinue = 0;
 17211  	SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
 17212  	SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
 17213  }
 17214  /*
 17215   * Allocate a new block instance.
 17216   * Return SXRET_OK and write a pointer to the new instantiated block
 17217   * on success.Otherwise generate a compile-time error and abort
 17218   * processing on failure.
 17219   */
 17220  static sxi32 GenStateEnterBlock(
 17221  	jx9_gen_state *pGen,  /* Code generator state */
 17222  	sxi32 iType,          /* Block type [i.e: loop, conditional, function body, etc.]*/
 17223  	sxu32 nFirstInstr,    /* First instruction to compile */
 17224  	void *pUserData,      /* Upper layer private data */
 17225  	GenBlock **ppBlock    /* OUT: instantiated block */
 17226  	)
 17227  {
 17228  	GenBlock *pBlock;
 17229  	/* Allocate a new block instance */
 17230  	pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
 17231  	if( pBlock == 0 ){
 17232  		/* If the supplied memory subsystem is so sick that we are unable to allocate
 17233  		 * a tiny chunk of memory, there is no much we can do here.
 17234  		 */
 17235  		return GenStateOutOfMem(pGen);
 17236  	}
 17237  	/* Zero the structure */
 17238  	SyZero(pBlock, sizeof(GenBlock));
 17239  	GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
 17240  	/* Link to the parent block */
 17241  	pBlock->pParent = pGen->pCurrent;
 17242  	/* Mark as the current block */
 17243  	pGen->pCurrent = pBlock;
 17244  	if( ppBlock ){
 17245  		/* Write a pointer to the new instance */
 17246  		*ppBlock = pBlock;
 17247  	}
 17248  	return SXRET_OK;
 17249  }
 17250  /*
 17251   * Release block fields without freeing the whole instance.
 17252   */
 17253  static void GenStateReleaseBlock(GenBlock *pBlock)
 17254  {
 17255  	SySetRelease(&pBlock->aPostContFix);
 17256  	SySetRelease(&pBlock->aJumpFix);
 17257  }
 17258  /*
 17259   * Release a block.
 17260   */
 17261  static void GenStateFreeBlock(GenBlock *pBlock)
 17262  {
 17263  	jx9_gen_state *pGen = pBlock->pGen;
 17264  	GenStateReleaseBlock(&(*pBlock));
 17265  	/* Free the instance */
 17266  	SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
 17267  }
 17268  /*
 17269   * POP and release a block from the stack of compiled blocks.
 17270   */
 17271  static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
 17272  {
 17273  	GenBlock *pBlock = pGen->pCurrent;
 17274  	if( pBlock == 0 ){
 17275  		/* No more block to pop */
 17276  		return SXERR_EMPTY;
 17277  	}
 17278  	/* Point to the upper block */
 17279  	pGen->pCurrent = pBlock->pParent;
 17280  	if( ppBlock ){
 17281  		/* Write a pointer to the popped block */
 17282  		*ppBlock = pBlock;
 17283  	}else{
 17284  		/* Safely release the block */
 17285  		GenStateFreeBlock(&(*pBlock));	
 17286  	}
 17287  	return SXRET_OK;
 17288  }
 17289  /*
 17290   * Emit a forward jump.
 17291   * Notes on forward jumps
 17292   *  Compilation of some JX9 constructs such as if, for, while and the logical or
 17293   *  (||) and logical and (&&) operators in expressions requires the
 17294   *  generation of forward jumps.
 17295   *  Since the destination PC target of these jumps isn't known when the jumps
 17296   *  are emitted, we record each forward jump in an instance of the following
 17297   *  structure. Those jumps are fixed later when the jump destination is resolved.
 17298   */
 17299  static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
 17300  {
 17301  	JumpFixup sJumpFix;
 17302  	sxi32 rc;
 17303  	/* Init the JumpFixup structure */
 17304  	sJumpFix.nJumpType = nJumpType;
 17305  	sJumpFix.nInstrIdx = nInstrIdx;
 17306  	/* Insert in the jump fixup table */
 17307  	rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
 17308  	return rc;
 17309  }
 17310  /*
 17311   * Fix a forward jump now the jump destination is resolved.
 17312   * Return the total number of fixed jumps.
 17313   * Notes on forward jumps:
 17314   *  Compilation of some JX9 constructs such as if, for, while and the logical or
 17315   *  (||) and logical and (&&) operators in expressions requires the
 17316   *  generation of forward jumps.
 17317   *  Since the destination PC target of these jumps isn't known when the jumps
 17318   *  are emitted, we record each forward jump in an instance of the following
 17319   *  structure.Those jumps are fixed later when the jump destination is resolved.
 17320   */
 17321  static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
 17322  {
 17323  	JumpFixup *aFix;
 17324  	VmInstr *pInstr;
 17325  	sxu32 nFixed; 
 17326  	sxu32 n;
 17327  	/* Point to the jump fixup table */
 17328  	aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
 17329  	/* Fix the desired jumps */
 17330  	for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
 17331  		if( aFix[n].nJumpType < 0 ){
 17332  			/* Already fixed */
 17333  			continue;
 17334  		}
 17335  		if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
 17336  			/* Not of our interest */
 17337  			continue;
 17338  		}
 17339  		/* Point to the instruction to fix */
 17340  		pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
 17341  		if( pInstr ){
 17342  			pInstr->iP2 = nJumpDest;
 17343  			nFixed++;
 17344  			/* Mark as fixed */
 17345  			aFix[n].nJumpType = -1;
 17346  		}
 17347  	}
 17348  	/* Total number of fixed jumps */
 17349  	return nFixed;
 17350  }
 17351  /*
 17352   * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
 17353   * in the constant table.
 17354   */
 17355  static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
 17356  {
 17357  	jx9_value *pObj;
 17358  	sxu32 nIdx = 0; /* cc warning */
 17359  	/* Reserve a new constant */
 17360  	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17361  	if( pObj == 0 ){
 17362  		GenStateOutOfMem(pGen);
 17363  		return 0;
 17364  	}
 17365  	*pIdx = nIdx;
 17366  	/* TODO(chems): Create a numeric table (64bit int keys) same as 
 17367  	 * the constant string iterals table [optimization purposes].
 17368  	 */
 17369  	return pObj;
 17370  }
 17371  /*
 17372   * Compile a numeric [i.e: integer or real] literal.
 17373   * Notes on the integer type.
 17374   *  According to the JX9 language reference manual
 17375   *  Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
 17376   *  or binary (base 2) notation, optionally preceded by a sign (- or +). 
 17377   *  To use octal notation, precede the number with a 0 (zero). To use hexadecimal 
 17378   *  notation precede the number with 0x. To use binary notation precede the number with 0b.
 17379   */
 17380  static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
 17381  {
 17382  	SyToken *pToken = pGen->pIn; /* Raw token */
 17383  	sxu32 nIdx = 0;
 17384  	if( pToken->nType & JX9_TK_INTEGER ){
 17385  		jx9_value *pObj;
 17386  		sxi64 iValue;
 17387  		iValue = jx9TokenValueToInt64(&pToken->sData);
 17388  		pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
 17389  		if( pObj == 0 ){
 17390  			SXUNUSED(iCompileFlag); /* cc warning */
 17391  			return SXERR_ABORT;
 17392  		}
 17393  		jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
 17394  	}else{
 17395  		/* Real number */
 17396  		jx9_value *pObj;
 17397  		/* Reserve a new constant */
 17398  		pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17399  		if( pObj == 0 ){
 17400  			return GenStateOutOfMem(pGen);
 17401  		}
 17402  		jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
 17403  		jx9MemObjToReal(pObj);
 17404  	}
 17405  	/* Emit the load constant instruction */
 17406  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17407  	/* Node successfully compiled */
 17408  	return SXRET_OK;
 17409  }
 17410  /*
 17411   * Compile a nowdoc string.
 17412   * According to the JX9 language reference manual:
 17413   *
 17414   *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
 17415   *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
 17416   *  The construct is ideal for embedding JX9 code or other large blocks of text without the
 17417   *  need for escaping. It shares some features in common with the SGML <![CDATA[ ]]> 
 17418   *  construct, in that it declares a block of text which is not for parsing.
 17419   *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
 17420   *  which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc 
 17421   *  identifiers also apply to nowdoc identifiers, especially those regarding the appearance
 17422   *  of the closing identifier. 
 17423   */
 17424  static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
 17425  {
 17426  	SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
 17427  	jx9_value *pObj;
 17428  	sxu32 nIdx;
 17429  	nIdx = 0; /* Prevent compiler warning */
 17430  	if( pStr->nByte <= 0 ){
 17431  		/* Empty string, load NULL */
 17432  		jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
 17433  		return SXRET_OK;
 17434  	}
 17435  	/* Reserve a new constant */
 17436  	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17437  	if( pObj == 0 ){
 17438  		jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
 17439  		SXUNUSED(iCompileFlag); /* cc warning */
 17440  		return SXERR_ABORT;
 17441  	}
 17442  	/* No processing is done here, simply a memcpy() operation */
 17443  	jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
 17444  	/* Emit the load constant instruction */
 17445  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17446  	/* Node successfully compiled */
 17447  	return SXRET_OK;
 17448  }
 17449  /*
 17450   * Compile a single quoted string.
 17451   * According to the JX9 language reference manual:
 17452   *
 17453   *   The simplest way to specify a string is to enclose it in single quotes (the character ' ).
 17454   *   To specify a literal single quote, escape it with a backslash (\). To specify a literal
 17455   *   backslash, double it (\\). All other instances of backslash will be treated as a literal
 17456   *   backslash: this means that the other escape sequences you might be used to, such as \r 
 17457   *   or \n, will be output literally as specified rather than having any special meaning.
 17458   * 
 17459   */
 17460  JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
 17461  {
 17462  	SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
 17463  	const char *zIn, *zCur, *zEnd;
 17464  	jx9_value *pObj;
 17465  	sxu32 nIdx;
 17466  	nIdx = 0; /* Prevent compiler warning */
 17467  	/* Delimit the string */
 17468  	zIn  = pStr->zString;
 17469  	zEnd = &zIn[pStr->nByte];
 17470  	if( zIn >= zEnd ){
 17471  		/* Empty string, load NULL */
 17472  		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
 17473  		return SXRET_OK;
 17474  	}
 17475  	if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
 17476  		/* Already processed, emit the load constant instruction
 17477  		 * and return.
 17478  		 */
 17479  		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17480  		return SXRET_OK;
 17481  	}
 17482  	/* Reserve a new constant */
 17483  	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17484  	if( pObj == 0 ){
 17485  		jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
 17486  		SXUNUSED(iCompileFlag); /* cc warning */
 17487  		return SXERR_ABORT;
 17488  	}
 17489  	jx9MemObjInitFromString(pGen->pVm, pObj, 0);
 17490  	/* Compile the node */
 17491  	for(;;){
 17492  		if( zIn >= zEnd ){
 17493  			/* End of input */
 17494  			break;
 17495  		}
 17496  		zCur = zIn;
 17497  		while( zIn < zEnd && zIn[0] != '\\' ){
 17498  			zIn++;
 17499  		}
 17500  		if( zIn > zCur ){
 17501  			/* Append raw contents*/
 17502  			jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
 17503  		}
 17504  		zIn++;
 17505  		if( zIn < zEnd ){
 17506  			if( zIn[0] == '\\' ){
 17507  				/* A literal backslash */
 17508  				jx9MemObjStringAppend(pObj, "\\", sizeof(char));
 17509  			}else if( zIn[0] == '\'' ){
 17510  				/* A single quote */
 17511  				jx9MemObjStringAppend(pObj, "'", sizeof(char));
 17512  			}else{
 17513  				/* verbatim copy */
 17514  				zIn--;
 17515  				jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
 17516  				zIn++;
 17517  			}
 17518  		}
 17519  		/* Advance the stream cursor */
 17520  		zIn++;
 17521  	}
 17522  	/* Emit the load constant instruction */
 17523  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17524  	if( pStr->nByte < 1024 ){
 17525  		/* Install in the literal table */
 17526  		GenStateInstallLiteral(pGen, pObj, nIdx);
 17527  	}
 17528  	/* Node successfully compiled */
 17529  	return SXRET_OK;
 17530  }
 17531  /*
 17532   * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
 17533   * According to the JX9 language reference manual
 17534   *   When a string is specified in double quotes or with heredoc, variables are parsed within it.
 17535   *  There are two types of syntax: a simple one and a complex one. The simple syntax is the most
 17536   *  common and convenient. It provides a way to embed a variable, an array value, or an object
 17537   *  property in a string with a minimum of effort.
 17538   *  Simple syntax
 17539   *   If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
 17540   *   to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
 17541   *   the end of the name.
 17542   *   Similarly, an array index or an object property can be parsed. With array indices, the closing
 17543   *   square bracket (]) marks the end of the index. The same rules apply to object properties
 17544   *   as to simple variables. 
 17545   *  Complex (curly) syntax
 17546   *   This isn't called complex because the syntax is complex, but because it allows for the use 
 17547   *   of complex expressions.
 17548   *   Any scalar variable, array element or object property with a string representation can be
 17549   *   included via this syntax. Simply write the expression the same way as it would appear outside
 17550   *   the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
 17551   *   be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
 17552   */
 17553  static sxi32 GenStateProcessStringExpression(
 17554  	jx9_gen_state *pGen, /* Code generator state */
 17555  	const char *zIn,     /* Raw expression */
 17556  	const char *zEnd     /* End of the expression */
 17557  	)
 17558  {
 17559  	SyToken *pTmpIn, *pTmpEnd;
 17560  	SySet sToken;
 17561  	sxi32 rc;
 17562  	/* Initialize the token set */
 17563  	SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
 17564  	/* Preallocate some slots */
 17565  	SySetAlloc(&sToken, 0x08);
 17566  	/* Tokenize the text */
 17567  	jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
 17568  	/* Swap delimiter */
 17569  	pTmpIn  = pGen->pIn;
 17570  	pTmpEnd = pGen->pEnd;
 17571  	pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
 17572  	pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
 17573  	/* Compile the expression */
 17574  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 17575  	/* Restore token stream */
 17576  	pGen->pIn  = pTmpIn;
 17577  	pGen->pEnd = pTmpEnd;
 17578  	/* Release the token set */
 17579  	SySetRelease(&sToken);
 17580  	/* Compilation result */
 17581  	return rc;
 17582  }
 17583  /*
 17584   * Reserve a new constant for a double quoted/heredoc string.
 17585   */
 17586  static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
 17587  {
 17588  	jx9_value *pConstObj;
 17589  	sxu32 nIdx = 0;
 17590  	/* Reserve a new constant */
 17591  	pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17592  	if( pConstObj == 0 ){
 17593  		GenStateOutOfMem(&(*pGen));
 17594  		return 0;
 17595  	}
 17596  	(*pCount)++;
 17597  	jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
 17598  	/* Emit the load constant instruction */
 17599  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17600  	return pConstObj;
 17601  }
 17602  /*
 17603   * Compile a double quoted/heredoc string.
 17604   * According to the JX9 language reference manual
 17605   * Heredoc
 17606   *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
 17607   *  is provided, then a newline. The string itself follows, and then the same identifier again
 17608   *  to close the quotation.
 17609   *  The closing identifier must begin in the first column of the line. Also, the identifier must
 17610   *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric
 17611   *  characters and underscores, and must start with a non-digit character or underscore.
 17612   *  Warning
 17613   *  It is very important to note that the line with the closing identifier must contain
 17614   *  no other characters, except possibly a semicolon (;). That means especially that the identifier
 17615   *  may not be indented, and there may not be any spaces or tabs before or after the semicolon.
 17616   *  It's also important to realize that the first character before the closing identifier must
 17617   *  be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
 17618   *  The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
 17619   *  If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
 17620   *  identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
 17621   *  the end of the current file, a parse error will result at the last line.
 17622   *  Heredocs can not be used for initializing object properties. 
 17623   * Double quoted
 17624   *  If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
 17625   *  Escaped characters Sequence 	Meaning
 17626   *  \n linefeed (LF or 0x0A (10) in ASCII)
 17627   *  \r carriage return (CR or 0x0D (13) in ASCII)
 17628   *  \t horizontal tab (HT or 0x09 (9) in ASCII)
 17629   *  \v vertical tab (VT or 0x0B (11) in ASCII)
 17630   *  \f form feed (FF or 0x0C (12) in ASCII)
 17631   *  \\ backslash
 17632   *  \$ dollar sign
 17633   *  \" double-quote
 17634   *  \[0-7]{1, 3} 	the sequence of characters matching the regular expression is a character in octal notation
 17635   *  \x[0-9A-Fa-f]{1, 2} 	the sequence of characters matching the regular expression is a character in hexadecimal notation
 17636   * As in single quoted strings, escaping any other character will result in the backslash being printed too.
 17637   * The most important feature of double-quoted strings is the fact that variable names will be expanded.
 17638   * See string parsing for details.
 17639   */
 17640  static sxi32 GenStateCompileString(jx9_gen_state *pGen)
 17641  {
 17642  	SyString *pStr = &pGen->pIn->sData; /* Raw token value */
 17643  	const char *zIn, *zCur, *zEnd;
 17644  	jx9_value *pObj = 0;
 17645  	sxi32 iCons;	
 17646  	sxi32 rc;
 17647  	/* Delimit the string */
 17648  	zIn  = pStr->zString;
 17649  	zEnd = &zIn[pStr->nByte];
 17650  	if( zIn >= zEnd ){
 17651  		/* Empty string, load NULL */
 17652  		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
 17653  		return SXRET_OK;
 17654  	}
 17655  	zCur = 0;
 17656  	/* Compile the node */
 17657  	iCons = 0;
 17658  	for(;;){
 17659  		zCur = zIn;
 17660  		while( zIn < zEnd && zIn[0] != '\\'  ){
 17661  			if(zIn[0] == '$' && &zIn[1] < zEnd &&
 17662  				(((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
 17663  					break;
 17664  			}
 17665  			zIn++;
 17666  		}
 17667  		if( zIn > zCur ){
 17668  			if( pObj == 0 ){
 17669  				pObj = GenStateNewStrObj(&(*pGen), &iCons);
 17670  				if( pObj == 0 ){
 17671  					return SXERR_ABORT;
 17672  				}
 17673  			}
 17674  			jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
 17675  		}
 17676  		if( zIn >= zEnd ){
 17677  			break;
 17678  		}
 17679  		if( zIn[0] == '\\' ){
 17680  			const char *zPtr = 0;
 17681  			sxu32 n;
 17682  			zIn++;
 17683  			if( zIn >= zEnd ){
 17684  				break;
 17685  			}
 17686  			if( pObj == 0 ){
 17687  				pObj = GenStateNewStrObj(&(*pGen), &iCons);
 17688  				if( pObj == 0 ){
 17689  					return SXERR_ABORT;
 17690  				}
 17691  			}
 17692  			n = sizeof(char); /* size of conversion */
 17693  			switch( zIn[0] ){
 17694  			case '$':
 17695  				/* Dollar sign */
 17696  				jx9MemObjStringAppend(pObj, "$", sizeof(char));
 17697  				break;
 17698  			case '\\':
 17699  				/* A literal backslash */
 17700  				jx9MemObjStringAppend(pObj, "\\", sizeof(char));
 17701  				break;
 17702  			case 'a':
 17703  				/* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
 17704  				jx9MemObjStringAppend(pObj, "\a", sizeof(char));
 17705  				break;
 17706  			case 'b':
 17707  				/* Backspace (BS)[ctrl+h] ASCII code 8 */
 17708  				jx9MemObjStringAppend(pObj, "\b", sizeof(char));
 17709  				break;
 17710  			case 'f':
 17711  				/* Form-feed (FF)[ctrl+l] ASCII code 12 */
 17712  				jx9MemObjStringAppend(pObj, "\f", sizeof(char));
 17713  				break;
 17714  			case 'n':
 17715  				/* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
 17716  				jx9MemObjStringAppend(pObj, "\n", sizeof(char));
 17717  				break;
 17718  			case 'r':
 17719  				/* Carriage return (CR)[ctrl+m] ASCII code 13 */
 17720  				jx9MemObjStringAppend(pObj, "\r", sizeof(char));
 17721  				break;
 17722  			case 't':
 17723  				/* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
 17724  				jx9MemObjStringAppend(pObj, "\t", sizeof(char));
 17725  				break;
 17726  			case 'v':
 17727  				/* Vertical tab(VT)[ctrl+k] ASCII code 11 */
 17728  				jx9MemObjStringAppend(pObj, "\v", sizeof(char));
 17729  				break;
 17730  			case '\'':
 17731  				/* Single quote */
 17732  				jx9MemObjStringAppend(pObj, "'", sizeof(char));
 17733  				break;
 17734  			case '"':
 17735  				/* Double quote */
 17736  				jx9MemObjStringAppend(pObj, "\"", sizeof(char));
 17737  				break;
 17738  			case '0':
 17739  				/* NUL byte */
 17740  				jx9MemObjStringAppend(pObj, "\0", sizeof(char));
 17741  				break;
 17742  			case 'x':
 17743  				if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
 17744  					int c;
 17745  					/* Hex digit */
 17746  					c = SyHexToint(zIn[1]) << 4;
 17747  					if( &zIn[2] < zEnd ){
 17748  						c +=  SyHexToint(zIn[2]);
 17749  					}
 17750  					/* Output char */
 17751  					jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
 17752  					n += sizeof(char) * 2;
 17753  				}else{
 17754  					/* Output literal character  */
 17755  					jx9MemObjStringAppend(pObj, "x", sizeof(char));
 17756  				}
 17757  				break;
 17758  			case 'o':
 17759  				if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
 17760  					/* Octal digit stream */
 17761  					int c;
 17762  					c = 0;
 17763  					zIn++;
 17764  					for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
 17765  						if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
 17766  							break;
 17767  						}
 17768  						c = c * 8 + (zPtr[0] - '0');
 17769  					}
 17770  					if ( c > 0 ){
 17771  						jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
 17772  					}
 17773  					n = (sxu32)(zPtr-zIn);
 17774  				}else{
 17775  					/* Output literal character  */
 17776  					jx9MemObjStringAppend(pObj, "o", sizeof(char));
 17777  				}
 17778  				break;
 17779  			default:
 17780  				/* Output without a slash */
 17781  				jx9MemObjStringAppend(pObj, zIn, sizeof(char));
 17782  				break;
 17783  			}
 17784  			/* Advance the stream cursor */
 17785  			zIn += n;
 17786  			continue;
 17787  		}
 17788  		if( zIn[0] == '{' ){
 17789  			/* Curly syntax */
 17790  			const char *zExpr;
 17791  			sxi32 iNest = 1;
 17792  			zIn++;
 17793  			zExpr = zIn;
 17794  			/* Synchronize with the next closing curly braces */
 17795  			while( zIn < zEnd ){
 17796  				if( zIn[0] == '{' ){
 17797  					/* Increment nesting level */
 17798  					iNest++;
 17799  				}else if(zIn[0] == '}' ){
 17800  					/* Decrement nesting level */
 17801  					iNest--;
 17802  					if( iNest <= 0 ){
 17803  						break;
 17804  					}
 17805  				}
 17806  				zIn++;
 17807  			}
 17808  			/* Process the expression */
 17809  			rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
 17810  			if( rc == SXERR_ABORT ){
 17811  				return SXERR_ABORT;
 17812  			}
 17813  			if( rc != SXERR_EMPTY ){
 17814  				++iCons;
 17815  			}
 17816  			if( zIn < zEnd ){
 17817  				/* Jump the trailing curly */
 17818  				zIn++;
 17819  			}
 17820  		}else{
 17821  			/* Simple syntax */
 17822  			const char *zExpr = zIn;
 17823  			/* Assemble variable name */
 17824  			for(;;){
 17825  				/* Jump leading dollars */
 17826  				while( zIn < zEnd && zIn[0] == '$' ){
 17827  					zIn++;
 17828  				}
 17829  				for(;;){
 17830  					while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
 17831  						zIn++;
 17832  					}
 17833  					if((unsigned char)zIn[0] >= 0xc0 ){
 17834  						/* UTF-8 stream */
 17835  						zIn++;
 17836  						while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
 17837  							zIn++;
 17838  						}
 17839  						continue;
 17840  					}
 17841  					break;
 17842  				}
 17843  				if( zIn >= zEnd ){
 17844  					break;
 17845  				}
 17846  				if( zIn[0] == '[' ){
 17847  					sxi32 iSquare = 1;
 17848  					zIn++;
 17849  					while( zIn < zEnd ){
 17850  						if( zIn[0] == '[' ){
 17851  							iSquare++;
 17852  						}else if (zIn[0] == ']' ){
 17853  							iSquare--;
 17854  							if( iSquare <= 0 ){
 17855  								break;
 17856  							}
 17857  						}
 17858  						zIn++;
 17859  					}
 17860  					if( zIn < zEnd ){
 17861  						zIn++;
 17862  					}
 17863  					break;
 17864  				}else if( zIn[0] == '.' ){
 17865  					/* Member access operator '.' */
 17866  					zIn++;
 17867  				}else{
 17868  					break;
 17869  				}
 17870  			}
 17871  			/* Process the expression */
 17872  			rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
 17873  			if( rc == SXERR_ABORT ){
 17874  				return SXERR_ABORT;
 17875  			}
 17876  			if( rc != SXERR_EMPTY ){
 17877  				++iCons;
 17878  			}
 17879  		}
 17880  		/* Invalidate the previously used constant */
 17881  		pObj = 0;
 17882  	}/*for(;;)*/
 17883  	if( iCons > 1 ){
 17884  		/* Concatenate all compiled constants */
 17885  		jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
 17886  	}
 17887  	/* Node successfully compiled */
 17888  	return SXRET_OK;
 17889  }
 17890  /*
 17891   * Compile a double quoted string.
 17892   *  See the block-comment above for more information.
 17893   */
 17894  JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
 17895  {
 17896  	sxi32 rc;
 17897  	rc = GenStateCompileString(&(*pGen));
 17898  	SXUNUSED(iCompileFlag); /* cc warning */
 17899  	/* Compilation result */
 17900  	return rc;
 17901  }
 17902  /*
 17903   * Compile a literal which is an identifier(name) for simple values.
 17904   */
 17905  JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
 17906  {
 17907  	SyToken *pToken = pGen->pIn;
 17908  	jx9_value *pObj;
 17909  	SyString *pStr;	
 17910  	sxu32 nIdx;
 17911  	/* Extract token value */
 17912  	pStr = &pToken->sData;
 17913  	/* Deal with the reserved literals [i.e: null, false, true, ...] first */
 17914  	if( pStr->nByte == sizeof("NULL") - 1 ){
 17915  		if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
 17916  			/* NULL constant are always indexed at 0 */
 17917  			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
 17918  			return SXRET_OK;
 17919  		}else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
 17920  			/* TRUE constant are always indexed at 1 */
 17921  			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
 17922  			return SXRET_OK;
 17923  		}
 17924  	}else if (pStr->nByte == sizeof("FALSE") - 1 &&
 17925  		SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
 17926  			/* FALSE constant are always indexed at 2 */
 17927  			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
 17928  			return SXRET_OK;
 17929  	}else if(pStr->nByte == sizeof("__LINE__") - 1 &&
 17930  		SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
 17931  			/* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
 17932  			pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17933  			if( pObj == 0 ){
 17934  				SXUNUSED(iCompileFlag); /* cc warning */
 17935  				return GenStateOutOfMem(pGen);
 17936  			}
 17937  			jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
 17938  			/* Emit the load constant instruction */
 17939  			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17940  			return SXRET_OK;
 17941  	}else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
 17942  		SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
 17943  			GenBlock *pBlock = pGen->pCurrent;
 17944  			/* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
 17945  			while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
 17946  				/* Point to the upper block */
 17947  				pBlock = pBlock->pParent;
 17948  			}
 17949  			if( pBlock == 0 ){
 17950  				/* Called in the global scope, load NULL */
 17951  				jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
 17952  			}else{
 17953  				/* Extract the target function/method */
 17954  				jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
 17955  				pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17956  				if( pObj == 0 ){
 17957  					return GenStateOutOfMem(pGen);
 17958  				}
 17959  				jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
 17960  				/* Emit the load constant instruction */
 17961  				jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 17962  			}
 17963  			return SXRET_OK;
 17964  	}
 17965  	/* Query literal table */
 17966  	if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
 17967  		jx9_value *pObj;
 17968  		/* Unknown literal, install it in the literal table */
 17969  		pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 17970  		if( pObj == 0 ){
 17971  			return GenStateOutOfMem(pGen);
 17972  		}
 17973  		jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
 17974  		GenStateInstallLiteral(&(*pGen), pObj, nIdx);
 17975  	}
 17976  	/* Emit the load constant instruction */
 17977  	jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
 17978  	/* Node successfully compiled */
 17979  	return SXRET_OK;
 17980  }
 17981  /*
 17982   * Compile an array entry whether it is a key or a value.
 17983   */
 17984  static sxi32 GenStateCompileJSONEntry(
 17985  	jx9_gen_state *pGen, /* Code generator state */
 17986  	SyToken *pIn,        /* Token stream */
 17987  	SyToken *pEnd,       /* End of the token stream */
 17988  	sxi32 iFlags,        /* Compilation flags */
 17989  	sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
 17990  	)
 17991  {
 17992  	SyToken *pTmpIn, *pTmpEnd;
 17993  	sxi32 rc;
 17994  	/* Swap token stream */
 17995  	SWAP_DELIMITER(pGen, pIn, pEnd);
 17996  	/* Compile the expression*/
 17997  	rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
 17998  	/* Restore token stream */
 17999  	RE_SWAP_DELIMITER(pGen);
 18000  	return rc;
 18001  }
 18002  /* 
 18003   * Compile a Jx9 JSON Array.
 18004   */
 18005  JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
 18006  {
 18007  	sxi32 nPair = 0;
 18008  	SyToken *pCur;
 18009  	sxi32 rc;
 18010  
 18011  	pGen->pIn++; /* Jump the open square bracket '['*/
 18012  	pGen->pEnd--;
 18013  	SXUNUSED(iCompileFlag); /* cc warning */
 18014  	for(;;){
 18015  		/* Jump leading commas */
 18016  		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
 18017  			pGen->pIn++;
 18018  		}
 18019  		pCur = pGen->pIn;
 18020  		if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
 18021  			/* No more entry to process */
 18022  			break;
 18023  		}
 18024  		/* Compile entry */
 18025  		rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
 18026  		if( rc == SXERR_ABORT ){
 18027  			return SXERR_ABORT;
 18028  		}
 18029  		nPair++;
 18030  	}
 18031  	/* Emit the load map instruction */
 18032  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
 18033  	/* Node successfully compiled */
 18034  	return SXRET_OK;
 18035  }
 18036  /*
 18037   * Node validator for a given JSON key.
 18038   */
 18039  static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
 18040  {
 18041  	sxi32 rc = SXRET_OK;
 18042  	if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString 
 18043  		&& pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
 18044  		/* Unexpected expression */
 18045  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0, 
 18046  			"JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
 18047  		if( rc != SXERR_ABORT ){
 18048  			rc = SXERR_INVALID;
 18049  		}
 18050  	}
 18051  	return rc;
 18052  }
 18053  /* 
 18054   * Compile a Jx9 JSON Object
 18055   */
 18056  JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
 18057  {
 18058  	SyToken *pKey, *pCur;
 18059  	sxi32 nPair = 0;
 18060  	sxi32 rc;
 18061  
 18062  	pGen->pIn++; /* Jump the open querly braces '{'*/
 18063  	pGen->pEnd--;
 18064  	SXUNUSED(iCompileFlag); /* cc warning */
 18065  	for(;;){
 18066  		/* Jump leading commas */
 18067  		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
 18068  			pGen->pIn++;
 18069  		}
 18070  		pCur = pGen->pIn;
 18071  		if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
 18072  			/* No more entry to process */
 18073  			break;
 18074  		}
 18075  		/* Compile the key */
 18076  		pKey = pCur;
 18077  		while( pCur < pGen->pIn ){
 18078  			if( pCur->nType & JX9_TK_COLON /*':'*/  ){
 18079  				break;
 18080  			}
 18081  			pCur++;
 18082  		}
 18083  		rc = SXERR_EMPTY;
 18084  		if( pCur < pGen->pIn ){
 18085  			if( &pCur[1] >= pGen->pIn ){
 18086  				/* Missing value */
 18087  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
 18088  				if( rc == SXERR_ABORT ){
 18089  					return SXERR_ABORT;
 18090  				}
 18091  				return SXRET_OK;
 18092  			}
 18093  			/* Compile the expression holding the key */
 18094  			rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
 18095  				EXPR_FLAG_RDONLY_LOAD                /* Do not create the variable if inexistant */, 
 18096  				GenStateJSONObjectKeyNodeValidator   /* Node validator callback */
 18097  				);
 18098  			if( rc == SXERR_ABORT ){
 18099  				return SXERR_ABORT;
 18100  			}
 18101  			pCur++; /* Jump the double colon ':'  */
 18102  		}else if( pKey == pCur ){
 18103  			/* Key is omitted, emit an error */
 18104  			jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
 18105  			pCur++; /* Jump the double colon ':'  */
 18106  		}else{
 18107  			/* Reset back the cursor and point to the entry value */
 18108  			pCur = pKey;
 18109  		}
 18110  		/* Compile indice value */
 18111  		rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
 18112  		if( rc == SXERR_ABORT ){
 18113  			return SXERR_ABORT;
 18114  		}
 18115  		nPair++;
 18116  	}
 18117  	/* Emit the load map instruction */
 18118  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
 18119  	/* Node successfully compiled */
 18120  	return SXRET_OK;
 18121  }
 18122  /*
 18123   * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
 18124   * construct.
 18125   */
 18126  JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
 18127  {
 18128  	SyString *pName;
 18129  	sxu32 nKeyID;
 18130  	sxi32 rc;
 18131  	/* Name of the language construct [i.e: print, die...]*/
 18132  	pName = &pGen->pIn->sData;
 18133  	nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
 18134  	pGen->pIn++; /* Jump the language construct keyword */
 18135  	if( nKeyID == JX9_TKWRD_PRINT ){
 18136  		SyToken *pTmp, *pNext = 0;
 18137  		/* Compile arguments one after one */
 18138  		pTmp = pGen->pEnd;
 18139  		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
 18140  		while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
 18141  			if( pGen->pIn < pNext ){
 18142  				pGen->pEnd = pNext;
 18143  				rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
 18144  				if( rc == SXERR_ABORT ){
 18145  					return SXERR_ABORT;
 18146  				}
 18147  				if( rc != SXERR_EMPTY ){
 18148  					/* Ticket 1433-008: Optimization #1: Consume input directly 
 18149  					 * without the overhead of a function call.
 18150  					 * This is a very powerful optimization that improve
 18151  					 * performance greatly.
 18152  					 */
 18153  					jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
 18154  				}
 18155  			}
 18156  			/* Jump trailing commas */
 18157  			while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
 18158  				pNext++;
 18159  			}
 18160  			pGen->pIn = pNext;
 18161  		}
 18162  		/* Restore token stream */
 18163  		pGen->pEnd = pTmp;	
 18164  	}else{
 18165  		sxi32 nArg = 0;
 18166  		sxu32 nIdx = 0;
 18167  		rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
 18168  		if( rc == SXERR_ABORT ){
 18169  			return SXERR_ABORT;
 18170  		}else if(rc != SXERR_EMPTY ){
 18171  			nArg = 1;
 18172  		}
 18173  		if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
 18174  			jx9_value *pObj;
 18175  			/* Emit the call instruction */
 18176  			pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 18177  			if( pObj == 0 ){
 18178  				SXUNUSED(iCompileFlag); /* cc warning */
 18179  				return GenStateOutOfMem(pGen);
 18180  			}
 18181  			jx9MemObjInitFromString(pGen->pVm, pObj, pName);
 18182  			/* Install in the literal table */
 18183  			GenStateInstallLiteral(&(*pGen), pObj, nIdx);
 18184  		}
 18185  		/* Emit the call instruction */
 18186  		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 18187  		jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
 18188  	}
 18189  	/* Node successfully compiled */
 18190  	return SXRET_OK;
 18191  }
 18192  /*
 18193   * Compile a node holding a variable declaration.
 18194   * According to the J9X language reference
 18195   *  Variables in JX9 are represented by a dollar sign followed by the name of the variable.
 18196   *  The variable name is case-sensitive.
 18197   *  Variable names follow the same rules as other labels in JX9. A valid variable name
 18198   *  starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
 18199   *  numbers, or underscores.
 18200   *  By default, variables are always assigned by value unless the target value is a JSON
 18201   *  array or a JSON object which is passed by reference.
 18202   */
 18203  JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
 18204  {
 18205  	sxu32 nLine = pGen->pIn->nLine;
 18206  	SyHashEntry *pEntry;
 18207  	SyString *pName;
 18208  	char *zName = 0;
 18209  	sxi32 iP1;
 18210  	void *p3;
 18211  	sxi32 rc;
 18212  	
 18213  	pGen->pIn++; /* Jump the dollar sign '$' */
 18214  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
 18215  		/* Invalid variable name */
 18216  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
 18217  		if( rc == SXERR_ABORT ){
 18218  			/* Error count limit reached, abort immediately */
 18219  			return SXERR_ABORT;
 18220  		}
 18221  		return SXRET_OK;
 18222  	}
 18223  	/* Extract variable name */
 18224  	pName = &pGen->pIn->sData;
 18225  	/* Advance the stream cursor */
 18226  	pGen->pIn++;
 18227  	pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
 18228  	if( pEntry == 0 ){
 18229  		/* Duplicate name */
 18230  		zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
 18231  		if( zName == 0 ){
 18232  			return GenStateOutOfMem(pGen);
 18233  		}
 18234  		/* Install in the hashtable */
 18235  		SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
 18236  	}else{
 18237  		/* Name already available */
 18238  		zName = (char *)pEntry->pUserData;
 18239  	}
 18240  	p3 = (void *)zName;	
 18241  	iP1 = 0;
 18242  	if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
 18243  		if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
 18244  			/* Read-only load.In other words do not create the variable if inexistant */
 18245  			iP1 = 1;
 18246  		}
 18247  	}
 18248  	/* Emit the load instruction */
 18249  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
 18250  	/* Node successfully compiled */
 18251  	return SXRET_OK;
 18252  }
 18253  /* Forward declaration */
 18254  static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
 18255  /*
 18256   * Compile an annoynmous function or a closure.
 18257   * According to the JX9 language reference
 18258   *  Anonymous functions, also known as closures, allow the creation of functions
 18259   *  which have no specified name. They are most useful as the value of callback
 18260   *  parameters, but they have many other uses. Closures can also be used as
 18261   *  the values of variables; Assigning a closure to a variable uses the same
 18262   *  syntax as any other assignment, including the trailing semicolon:
 18263   *  Example Anonymous function variable assignment example
 18264   * $greet = function($name)
 18265   * {
 18266   *    printf("Hello %s\r\n", $name);
 18267   * };
 18268   * $greet('World');
 18269   * $greet('JX9');
 18270   * Note that the implementation of annoynmous function and closure under
 18271   * JX9 is completely different from the one used by the  engine.
 18272   */
 18273  JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
 18274  {
 18275  	jx9_vm_func *pAnnonFunc; /* Annonymous function body */
 18276  	char zName[512];         /* Unique lambda name */
 18277  	static int iCnt = 1;     /* There is no worry about thread-safety here, because only
 18278  							  * one thread is allowed to compile the script.
 18279  						      */
 18280  	jx9_value *pObj;
 18281  	SyString sName;
 18282  	sxu32 nIdx;
 18283  	sxu32 nLen;
 18284  	sxi32 rc;
 18285  
 18286  	pGen->pIn++; /* Jump the 'function' keyword */
 18287  	if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
 18288  		pGen->pIn++;
 18289  	}
 18290  	/* Reserve a constant for the lambda */
 18291  	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
 18292  	if( pObj == 0 ){
 18293  		GenStateOutOfMem(pGen);
 18294  		SXUNUSED(iCompileFlag); /* cc warning */
 18295  		return SXERR_ABORT;
 18296  	}
 18297  	/* Generate a unique name */
 18298  	nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
 18299  	/* Make sure the generated name is unique */
 18300  	while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
 18301  		nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
 18302  	}
 18303  	SyStringInitFromBuf(&sName, zName, nLen);
 18304  	jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
 18305  	/* Compile the lambda body */
 18306  	rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
 18307  	if( rc == SXERR_ABORT ){
 18308  		return SXERR_ABORT;
 18309  	}
 18310  	/* Emit the load constant instruction */
 18311  	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
 18312  	/* Node successfully compiled */
 18313  	return SXRET_OK;
 18314  }
 18315  /*
 18316   * Compile the 'continue' statement.
 18317   * According to the JX9 language reference
 18318   *  continue is used within looping structures to skip the rest of the current loop iteration
 18319   *  and continue execution at the condition evaluation and then the beginning of the next
 18320   *  iteration.
 18321   *  Note: Note that in JX9 the switch statement is considered a looping structure for
 18322   *  the purposes of continue. 
 18323   *  continue accepts an optional numeric argument which tells it how many levels
 18324   *  of enclosing loops it should skip to the end of.
 18325   *  Note:
 18326   *   continue 0; and continue 1; is the same as running continue;. 
 18327   */
 18328  static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
 18329  {
 18330  	GenBlock *pLoop; /* Target loop */
 18331  	sxi32 iLevel;    /* How many nesting loop to skip */
 18332  	sxu32 nLine;
 18333  	sxi32 rc;
 18334  	nLine = pGen->pIn->nLine;
 18335  	iLevel = 0;
 18336  	/* Jump the 'continue' keyword */
 18337  	pGen->pIn++;
 18338  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
 18339  		/* optional numeric argument which tells us how many levels
 18340  		 * of enclosing loops we should skip to the end of. 
 18341  		 */
 18342  		iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
 18343  		if( iLevel < 2 ){
 18344  			iLevel = 0;
 18345  		}
 18346  		pGen->pIn++; /* Jump the optional numeric argument */
 18347  	}
 18348  	/* Point to the target loop */
 18349  	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
 18350  	if( pLoop == 0 ){
 18351  		/* Illegal continue */
 18352  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
 18353  		if( rc == SXERR_ABORT ){
 18354  			/* Error count limit reached, abort immediately */
 18355  			return SXERR_ABORT;
 18356  		}
 18357  	}else{
 18358  		sxu32 nInstrIdx = 0;
 18359  		/* Emit the unconditional jump to the beginning of the target loop */
 18360  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
 18361  		if( pLoop->bPostContinue == TRUE ){
 18362  			JumpFixup sJumpFix;
 18363  			/* Post-continue */
 18364  			sJumpFix.nJumpType = JX9_OP_JMP;
 18365  			sJumpFix.nInstrIdx = nInstrIdx;
 18366  			SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
 18367  		}
 18368  	}
 18369  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 18370  		/* Not so fatal, emit a warning only */
 18371  		jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
 18372  	}
 18373  	/* Statement successfully compiled */
 18374  	return SXRET_OK;
 18375  }
 18376  /*
 18377   * Compile the 'break' statement.
 18378   * According to the JX9 language reference
 18379   *  break ends execution of the current for, foreach, while, do-while or switch
 18380   *  structure.
 18381   *  break accepts an optional numeric argument which tells it how many nested
 18382   *  enclosing structures are to be broken out of. 
 18383   */
 18384  static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
 18385  {
 18386  	GenBlock *pLoop; /* Target loop */
 18387  	sxi32 iLevel;    /* How many nesting loop to skip */
 18388  	sxu32 nLine;
 18389  	sxi32 rc;
 18390  	nLine = pGen->pIn->nLine;
 18391  	iLevel = 0;
 18392  	/* Jump the 'break' keyword */
 18393  	pGen->pIn++;
 18394  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
 18395  		/* optional numeric argument which tells us how many levels
 18396  		 * of enclosing loops we should skip to the end of. 
 18397  		 */
 18398  		iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
 18399  		if( iLevel < 2 ){
 18400  			iLevel = 0;
 18401  		}
 18402  		pGen->pIn++; /* Jump the optional numeric argument */
 18403  	}
 18404  	/* Extract the target loop */
 18405  	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
 18406  	if( pLoop == 0 ){
 18407  		/* Illegal break */
 18408  		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
 18409  		if( rc == SXERR_ABORT ){
 18410  			/* Error count limit reached, abort immediately */
 18411  			return SXERR_ABORT;
 18412  		}
 18413  	}else{
 18414  		sxu32 nInstrIdx; 
 18415  		rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
 18416  		if( rc == SXRET_OK ){
 18417  			/* Fix the jump later when the jump destination is resolved */
 18418  			GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
 18419  		}
 18420  	}
 18421  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 18422  		/* Not so fatal, emit a warning only */
 18423  		jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
 18424  	}
 18425  	/* Statement successfully compiled */
 18426  	return SXRET_OK;
 18427  }
 18428  /* Forward declaration */
 18429  static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
 18430  /*
 18431   * Compile a JX9 block.
 18432   * A block is simply one or more JX9 statements and expressions to compile
 18433   * optionally delimited by braces {}.
 18434   * Return SXRET_OK on success. Any other return value indicates failure
 18435   * and this function takes care of generating the appropriate error
 18436   * message.
 18437   */
 18438  static sxi32 jx9CompileBlock(
 18439  	jx9_gen_state *pGen /* Code generator state */
 18440  	)
 18441  {
 18442  	sxi32 rc;
 18443  	if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
 18444  		sxu32 nLine = pGen->pIn->nLine;
 18445  		rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
 18446  		if( rc != SXRET_OK ){
 18447  			return SXERR_ABORT;
 18448  		}
 18449  		pGen->pIn++;
 18450  		/* Compile until we hit the closing braces '}' */
 18451  		for(;;){
 18452  			if( pGen->pIn >= pGen->pEnd ){
 18453  				/* No more token to process. Missing closing braces */
 18454  				jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
 18455  				break;
 18456  			}
 18457  			if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
 18458  				/* Closing braces found, break immediately*/
 18459  				pGen->pIn++;
 18460  				break;
 18461  			}
 18462  			/* Compile a single statement */
 18463  			rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
 18464  			if( rc == SXERR_ABORT ){
 18465  				return SXERR_ABORT;
 18466  			}
 18467  		}
 18468  		GenStateLeaveBlock(&(*pGen), 0);			
 18469  	}else{
 18470  		/* Compile a single statement */
 18471  		rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
 18472  		if( rc == SXERR_ABORT ){
 18473  			return SXERR_ABORT;
 18474  		}
 18475  	}
 18476  	/* Jump trailing semi-colons ';' */
 18477  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
 18478  		pGen->pIn++;
 18479  	}
 18480  	return SXRET_OK;
 18481  }
 18482  /*
 18483   * Compile the gentle 'while' statement.
 18484   * According to the JX9 language reference
 18485   *  while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
 18486   *  The basic form of a while statement is:
 18487   *  while (expr)
 18488   *   statement
 18489   *  The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
 18490   *  repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
 18491   *  is checked each time at the beginning of the loop, so even if this value changes during
 18492   *  the execution of the nested statement(s), execution will not stop until the end of the iteration
 18493   *  (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
 18494   *  expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
 18495   *  Like with the if statement, you can group multiple statements within the same while loop by surrounding
 18496   *  a group of statements with curly braces, or by using the alternate syntax:
 18497   *  while (expr):
 18498   *    statement
 18499   *   endwhile;
 18500   */
 18501  static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
 18502  { 
 18503  	GenBlock *pWhileBlock = 0;
 18504  	SyToken *pTmp, *pEnd = 0;
 18505  	sxu32 nFalseJump;
 18506  	sxu32 nLine;
 18507  	sxi32 rc;
 18508  	nLine = pGen->pIn->nLine;
 18509  	/* Jump the 'while' keyword */
 18510  	pGen->pIn++;    
 18511  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
 18512  		/* Syntax error */
 18513  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
 18514  		if( rc == SXERR_ABORT ){
 18515  			/* Error count limit reached, abort immediately */
 18516  			return SXERR_ABORT;
 18517  		}
 18518  		goto Synchronize;
 18519  	}
 18520  	/* Jump the left parenthesis '(' */
 18521  	pGen->pIn++; 
 18522  	/* Create the loop block */
 18523  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
 18524  	if( rc != SXRET_OK ){
 18525  		return SXERR_ABORT;
 18526  	}
 18527  	/* Delimit the condition */
 18528  	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 18529  	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
 18530  		/* Empty expression */
 18531  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
 18532  		if( rc == SXERR_ABORT ){
 18533  			/* Error count limit reached, abort immediately */
 18534  			return SXERR_ABORT;
 18535  		}
 18536  	}
 18537  	/* Swap token streams */
 18538  	pTmp = pGen->pEnd;
 18539  	pGen->pEnd = pEnd;
 18540  	/* Compile the expression */
 18541  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 18542  	if( rc == SXERR_ABORT ){
 18543  		/* Expression handler request an operation abort [i.e: Out-of-memory] */
 18544  		return SXERR_ABORT;
 18545  	}
 18546  	/* Update token stream */
 18547  	while(pGen->pIn < pEnd ){
 18548  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
 18549  		if( rc == SXERR_ABORT ){
 18550  			return SXERR_ABORT;
 18551  		}
 18552  		pGen->pIn++;
 18553  	}
 18554  	/* Synchronize pointers */
 18555  	pGen->pIn  = &pEnd[1];
 18556  	pGen->pEnd = pTmp;
 18557  	/* Emit the false jump */
 18558  	jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
 18559  	/* Save the instruction index so we can fix it later when the jump destination is resolved */
 18560  	GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
 18561  	/* Compile the loop body */
 18562  	rc = jx9CompileBlock(&(*pGen));
 18563  	if( rc == SXERR_ABORT ){
 18564  		return SXERR_ABORT;
 18565  	}
 18566  	/* Emit the unconditional jump to the start of the loop */
 18567  	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
 18568  	/* Fix all jumps now the destination is resolved */
 18569  	GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
 18570  	/* Release the loop block */
 18571  	GenStateLeaveBlock(pGen, 0);
 18572  	/* Statement successfully compiled */
 18573  	return SXRET_OK;
 18574  Synchronize:
 18575  	/* Synchronize with the first semi-colon ';' so we can avoid 
 18576  	 * compiling this erroneous block.
 18577  	 */
 18578  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
 18579  		pGen->pIn++;
 18580  	}
 18581  	return SXRET_OK;
 18582  }
 18583  /*
 18584   * Compile the complex and powerful 'for' statement.
 18585   * According to the JX9 language reference
 18586   *  for loops are the most complex loops in JX9. They behave like their C counterparts.
 18587   *  The syntax of a for loop is:
 18588   *  for (expr1; expr2; expr3)
 18589   *   statement
 18590   *  The first expression (expr1) is evaluated (executed) once unconditionally at
 18591   *  the beginning of the loop.
 18592   *  In the beginning of each iteration, expr2 is evaluated. If it evaluates to
 18593   *  TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
 18594   *  to FALSE, the execution of the loop ends.
 18595   *  At the end of each iteration, expr3 is evaluated (executed).
 18596   *  Each of the expressions can be empty or contain multiple expressions separated by commas.
 18597   *  In expr2, all expressions separated by a comma are evaluated but the result is taken
 18598   *  from the last part. expr2 being empty means the loop should be run indefinitely
 18599   *  (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
 18600   *  think, since often you'd want to end the loop using a conditional break statement instead
 18601   *  of using the for truth expression.
 18602   */
 18603  static sxi32 jx9CompileFor(jx9_gen_state *pGen)
 18604  {
 18605  	SyToken *pTmp, *pPostStart, *pEnd = 0;
 18606  	GenBlock *pForBlock = 0;
 18607  	sxu32 nFalseJump;
 18608  	sxu32 nLine;
 18609  	sxi32 rc;
 18610  	nLine = pGen->pIn->nLine;
 18611  	/* Jump the 'for' keyword */
 18612  	pGen->pIn++;    
 18613  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
 18614  		/* Syntax error */
 18615  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
 18616  		if( rc == SXERR_ABORT ){
 18617  			/* Error count limit reached, abort immediately */
 18618  			return SXERR_ABORT;
 18619  		}
 18620  		return SXRET_OK;
 18621  	}
 18622  	/* Jump the left parenthesis '(' */
 18623  	pGen->pIn++; 
 18624  	/* Delimit the init-expr;condition;post-expr */
 18625  	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 18626  	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
 18627  		/* Empty expression */
 18628  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
 18629  		if( rc == SXERR_ABORT ){
 18630  			/* Error count limit reached, abort immediately */
 18631  			return SXERR_ABORT;
 18632  		}
 18633  		/* Synchronize */
 18634  		pGen->pIn = pEnd;
 18635  		if( pGen->pIn < pGen->pEnd ){
 18636  			pGen->pIn++;
 18637  		}
 18638  		return SXRET_OK;
 18639  	}
 18640  	/* Swap token streams */
 18641  	pTmp = pGen->pEnd;
 18642  	pGen->pEnd = pEnd;
 18643  	/* Compile initialization expressions if available */
 18644  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 18645  	/* Pop operand lvalues */
 18646  	if( rc == SXERR_ABORT ){
 18647  		/* Expression handler request an operation abort [i.e: Out-of-memory] */
 18648  		return SXERR_ABORT;
 18649  	}else if( rc != SXERR_EMPTY ){
 18650  		jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
 18651  	}
 18652  	if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 18653  		/* Syntax error */
 18654  		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, 
 18655  			"for: Expected ';' after initialization expressions");
 18656  		if( rc == SXERR_ABORT ){
 18657  			/* Error count limit reached, abort immediately */
 18658  			return SXERR_ABORT;
 18659  		}
 18660  		return SXRET_OK;
 18661  	}
 18662  	/* Jump the trailing ';' */
 18663  	pGen->pIn++;
 18664  	/* Create the loop block */
 18665  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
 18666  	if( rc != SXRET_OK ){
 18667  		return SXERR_ABORT;
 18668  	}
 18669  	/* Deffer continue jumps */
 18670  	pForBlock->bPostContinue = TRUE;
 18671  	/* Compile the condition */
 18672  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 18673  	if( rc == SXERR_ABORT ){
 18674  		/* Expression handler request an operation abort [i.e: Out-of-memory] */
 18675  		return SXERR_ABORT;
 18676  	}else if( rc != SXERR_EMPTY ){
 18677  		/* Emit the false jump */
 18678  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
 18679  		/* Save the instruction index so we can fix it later when the jump destination is resolved */
 18680  		GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
 18681  	}
 18682  	if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 18683  		/* Syntax error */
 18684  		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, 
 18685  			"for: Expected ';' after conditionals expressions");
 18686  		if( rc == SXERR_ABORT ){
 18687  			/* Error count limit reached, abort immediately */
 18688  			return SXERR_ABORT;
 18689  		}
 18690  		return SXRET_OK;
 18691  	}
 18692  	/* Jump the trailing ';' */
 18693  	pGen->pIn++;
 18694  	/* Save the post condition stream */
 18695  	pPostStart = pGen->pIn;
 18696  	/* Compile the loop body */
 18697  	pGen->pIn  = &pEnd[1]; /* Jump the trailing parenthesis ')' */
 18698  	pGen->pEnd = pTmp;
 18699  	rc = jx9CompileBlock(&(*pGen));
 18700  	if( rc == SXERR_ABORT ){
 18701  		return SXERR_ABORT;
 18702  	}
 18703  	/* Fix post-continue jumps */
 18704  	if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
 18705  		JumpFixup *aPost;
 18706  		VmInstr *pInstr;
 18707  		sxu32 nJumpDest;
 18708  		sxu32 n;
 18709  		aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
 18710  		nJumpDest = jx9VmInstrLength(pGen->pVm);
 18711  		for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
 18712  			pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
 18713  			if( pInstr ){
 18714  				/* Fix jump */
 18715  				pInstr->iP2 = nJumpDest;
 18716  			}
 18717  		}
 18718  	}
 18719  	/* compile the post-expressions if available */
 18720  	while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
 18721  		pPostStart++;
 18722  	}
 18723  	if( pPostStart < pEnd ){
 18724  		SyToken *pTmpIn, *pTmpEnd;
 18725  		SWAP_DELIMITER(pGen, pPostStart, pEnd);
 18726  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 18727  		if( pGen->pIn < pGen->pEnd ){
 18728  			/* Syntax error */
 18729  			rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
 18730  			if( rc == SXERR_ABORT ){
 18731  				/* Error count limit reached, abort immediately */
 18732  				return SXERR_ABORT;
 18733  			}
 18734  			return SXRET_OK;
 18735  		}
 18736  		RE_SWAP_DELIMITER(pGen);
 18737  		if( rc == SXERR_ABORT ){
 18738  			/* Expression handler request an operation abort [i.e: Out-of-memory] */
 18739  			return SXERR_ABORT;
 18740  		}else if( rc != SXERR_EMPTY){
 18741  			/* Pop operand lvalue */
 18742  			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
 18743  		}
 18744  	}
 18745  	/* Emit the unconditional jump to the start of the loop */
 18746  	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
 18747  	/* Fix all jumps now the destination is resolved */
 18748  	GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
 18749  	/* Release the loop block */
 18750  	GenStateLeaveBlock(pGen, 0);
 18751  	/* Statement successfully compiled */
 18752  	return SXRET_OK;
 18753  }
 18754  /* Expression tree validator callback used by the 'foreach' statement.
 18755   * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
 18756   * are allowed.
 18757   */
 18758  static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
 18759  {
 18760  	sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
 18761  	if( pRoot->xCode != jx9CompileVariable ){
 18762  		/* Unexpected expression */
 18763  		rc = jx9GenCompileError(&(*pGen),
 18764  			E_ERROR,
 18765  			pRoot->pStart? pRoot->pStart->nLine : 0, 
 18766  			"foreach: Expecting a variable name"
 18767  			);
 18768  		if( rc != SXERR_ABORT ){
 18769  			rc = SXERR_INVALID;
 18770  		}
 18771  	}
 18772  	return rc;
 18773  }
 18774  /*
 18775   * Compile the 'foreach' statement.
 18776   * According to the JX9 language reference
 18777   *  The foreach construct simply gives an easy way to iterate over arrays. foreach works
 18778   *  only on arrays (and objects), and will issue an error when you try to use it on a variable
 18779   *  with a different data type or an uninitialized variable. There are two syntaxes; the second
 18780   *  is a minor but useful extension of the first:
 18781   *  foreach (json_array_json_object as $value)
 18782   *    statement
 18783   *  foreach (json_array_json_objec as $key,$value)
 18784   *   statement
 18785   *  The first form loops over the array given by array_expression. On each loop, the value 
 18786   *  of the current element is assigned to $value and the internal array pointer is advanced
 18787   *  by one (so on the next loop, you'll be looking at the next element).
 18788   *  The second form does the same thing, except that the current element's key will be assigned
 18789   *  to the variable $key on each loop.
 18790   *  Note:
 18791   *  When foreach first starts executing, the internal array pointer is automatically reset to the
 18792   *  first element of the array. This means that you do not need to call reset() before a foreach loop. 
 18793   */
 18794  static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
 18795  { 
 18796  	SyToken *pCur, *pTmp, *pEnd = 0;
 18797  	GenBlock *pForeachBlock = 0;
 18798  	jx9_foreach_info *pInfo;
 18799  	sxu32 nFalseJump;
 18800  	VmInstr *pInstr;
 18801  	sxu32 nLine;
 18802  	sxi32 rc;
 18803  	nLine = pGen->pIn->nLine;
 18804  	/* Jump the 'foreach' keyword */
 18805  	pGen->pIn++;    
 18806  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
 18807  		/* Syntax error */
 18808  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
 18809  		if( rc == SXERR_ABORT ){
 18810  			/* Error count limit reached, abort immediately */
 18811  			return SXERR_ABORT;
 18812  		}
 18813  		goto Synchronize;
 18814  	}
 18815  	/* Jump the left parenthesis '(' */
 18816  	pGen->pIn++; 
 18817  	/* Create the loop block */
 18818  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
 18819  	if( rc != SXRET_OK ){
 18820  		return SXERR_ABORT;
 18821  	}
 18822  	/* Delimit the expression */
 18823  	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 18824  	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
 18825  		/* Empty expression */
 18826  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
 18827  		if( rc == SXERR_ABORT ){
 18828  			/* Error count limit reached, abort immediately */
 18829  			return SXERR_ABORT;
 18830  		}
 18831  		/* Synchronize */
 18832  		pGen->pIn = pEnd;
 18833  		if( pGen->pIn < pGen->pEnd ){
 18834  			pGen->pIn++;
 18835  		}
 18836  		return SXRET_OK;
 18837  	}
 18838  	/* Compile the array expression */
 18839  	pCur = pGen->pIn;
 18840  	while( pCur < pEnd ){
 18841  		if( pCur->nType & JX9_TK_KEYWORD ){
 18842  			sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
 18843  			if( nKeywrd == JX9_TKWRD_AS ){
 18844  				/* Break with the first 'as' found */
 18845  				break;
 18846  			}
 18847  		}
 18848  		/* Advance the stream cursor */
 18849  		pCur++;
 18850  	}
 18851  	if( pCur <= pGen->pIn ){
 18852  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, 
 18853  			"foreach: Missing array/object expression");
 18854  		if( rc == SXERR_ABORT ){
 18855  			/* Don't worry about freeing memory, everything will be released shortly */
 18856  			return SXERR_ABORT;
 18857  		}
 18858  		goto Synchronize;
 18859  	}
 18860  	/* Swap token streams */
 18861  	pTmp = pGen->pEnd;
 18862  	pGen->pEnd = pCur;
 18863  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 18864  	if( rc == SXERR_ABORT ){
 18865  		/* Expression handler request an operation abort [i.e: Out-of-memory] */
 18866  		return SXERR_ABORT;
 18867  	}
 18868  	/* Update token stream */
 18869  	while(pGen->pIn < pCur ){
 18870  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
 18871  		if( rc == SXERR_ABORT ){
 18872  			/* Don't worry about freeing memory, everything will be released shortly */
 18873  			return SXERR_ABORT;
 18874  		}
 18875  		pGen->pIn++;
 18876  	}
 18877  	pCur++; /* Jump the 'as' keyword */
 18878  	pGen->pIn = pCur; 
 18879  	if( pGen->pIn >= pEnd ){
 18880  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
 18881  		if( rc == SXERR_ABORT ){
 18882  			return SXERR_ABORT;
 18883  		}
 18884  	}
 18885  	/* Create the foreach context */
 18886  	pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
 18887  	if( pInfo == 0 ){
 18888  		jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
 18889  		return SXERR_ABORT;
 18890  	}
 18891  	/* Zero the structure */
 18892  	SyZero(pInfo, sizeof(jx9_foreach_info));
 18893  	/* Initialize structure fields */
 18894  	SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
 18895  	/* Check if we have a key field */
 18896  	while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
 18897  		pCur++;
 18898  	}
 18899  	if( pCur < pEnd ){
 18900  		/* Compile the expression holding the key name */
 18901  		if( pGen->pIn >= pCur ){
 18902  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
 18903  			if( rc == SXERR_ABORT ){
 18904  				/* Don't worry about freeing memory, everything will be released shortly */
 18905  				return SXERR_ABORT;
 18906  			}
 18907  		}else{
 18908  			pGen->pEnd = pCur;
 18909  			rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
 18910  			if( rc == SXERR_ABORT ){
 18911  				/* Don't worry about freeing memory, everything will be released shortly */
 18912  				return SXERR_ABORT;
 18913  			}
 18914  			pInstr = jx9VmPopInstr(pGen->pVm);
 18915  			if( pInstr->p3 ){
 18916  				/* Record key name */
 18917  				SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
 18918  			}
 18919  			pInfo->iFlags |= JX9_4EACH_STEP_KEY;
 18920  		}
 18921  		pGen->pIn = &pCur[1]; /* Jump the arrow */
 18922  	}
 18923  	pGen->pEnd = pEnd;
 18924  	if( pGen->pIn >= pEnd ){
 18925  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
 18926  		if( rc == SXERR_ABORT ){
 18927  			/* Don't worry about freeing memory, everything will be released shortly */
 18928  			return SXERR_ABORT;
 18929  		}
 18930  		goto Synchronize;
 18931  	}
 18932  	/* Compile the expression holding the value name */
 18933  	rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
 18934  	if( rc == SXERR_ABORT ){
 18935  		/* Don't worry about freeing memory, everything will be released shortly */
 18936  		return SXERR_ABORT;
 18937  	}
 18938  	pInstr = jx9VmPopInstr(pGen->pVm);
 18939  	if( pInstr->p3 ){
 18940  		/* Record value name */
 18941  		SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
 18942  	}
 18943  	/* Emit the 'FOREACH_INIT' instruction */
 18944  	jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
 18945  	/* Save the instruction index so we can fix it later when the jump destination is resolved */
 18946  	GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
 18947  	/* Record the first instruction to execute */
 18948  	pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
 18949  	/* Emit the FOREACH_STEP instruction */
 18950      jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
 18951  	/* Save the instruction index so we can fix it later when the jump destination is resolved */
 18952  	GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
 18953  	/* Compile the loop body */
 18954  	pGen->pIn = &pEnd[1];
 18955  	pGen->pEnd = pTmp;
 18956  	rc = jx9CompileBlock(&(*pGen));
 18957  	if( rc == SXERR_ABORT ){
 18958  		/* Don't worry about freeing memory, everything will be released shortly */
 18959  		return SXERR_ABORT;
 18960  	}
 18961  	/* Emit the unconditional jump to the start of the loop */
 18962  	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
 18963  	/* Fix all jumps now the destination is resolved */
 18964  	GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
 18965  	/* Release the loop block */
 18966  	GenStateLeaveBlock(pGen, 0);
 18967  	/* Statement successfully compiled */
 18968  	return SXRET_OK;
 18969  Synchronize:
 18970  	/* Synchronize with the first semi-colon ';' so we can avoid 
 18971  	 * compiling this erroneous block.
 18972  	 */
 18973  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
 18974  		pGen->pIn++;
 18975  	}
 18976  	return SXRET_OK;
 18977  }
 18978  /*
 18979   * Compile the infamous if/elseif/else if/else statements.
 18980   * According to the JX9 language reference
 18981   *  The if construct is one of the most important features of many languages JX9 included.
 18982   *  It allows for conditional execution of code fragments. JX9 features an if structure 
 18983   *  that is similar to that of C:
 18984   *  if (expr)
 18985   *   statement
 18986   *  else construct:
 18987   *   Often you'd want to execute a statement if a certain condition is met, and a different
 18988   *   statement if the condition is not met. This is what else is for. else extends an if statement
 18989   *   to execute a statement in case the expression in the if statement evaluates to FALSE.
 18990   *   For example, the following code would display a is greater than b if $a is greater than
 18991   *   $b, and a is NOT greater than b otherwise.
 18992   *   The else statement is only executed if the if expression evaluated to FALSE, and if there
 18993   *   were any elseif expressions - only if they evaluated to FALSE as well
 18994   *  elseif
 18995   *   elseif, as its name suggests, is a combination of if and else. Like else, it extends
 18996   *   an if statement to execute a different statement in case the original if expression evaluates
 18997   *   to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
 18998   *   conditional expression evaluates to TRUE. For example, the following code would display a is bigger
 18999   *   than b, a equal to b or a is smaller than b:
 19000   *    if ($a > $b) {
 19001   *     print "a is bigger than b";
 19002   *    } elseif ($a == $b) {
 19003   *     print "a is equal to b";
 19004   *    } else {
 19005   *     print "a is smaller than b";
 19006   *    }
 19007   */
 19008  static sxi32 jx9CompileIf(jx9_gen_state *pGen)
 19009  {
 19010  	SyToken *pToken, *pTmp, *pEnd = 0;
 19011  	GenBlock *pCondBlock = 0;
 19012  	sxu32 nJumpIdx;
 19013  	sxu32 nKeyID;
 19014  	sxi32 rc;
 19015  	/* Jump the 'if' keyword */
 19016  	pGen->pIn++;
 19017  	pToken = pGen->pIn; 
 19018  	/* Create the conditional block */
 19019  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
 19020  	if( rc != SXRET_OK ){
 19021  		return SXERR_ABORT;
 19022  	}
 19023  	/* Process as many [if/else if/elseif/else] blocks as we can */
 19024  	for(;;){
 19025  		if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
 19026  			/* Syntax error */
 19027  			if( pToken >= pGen->pEnd ){
 19028  				pToken--;
 19029  			}
 19030  			rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
 19031  			if( rc == SXERR_ABORT ){
 19032  				/* Error count limit reached, abort immediately */
 19033  				return SXERR_ABORT;
 19034  			}
 19035  			goto Synchronize;
 19036  		}
 19037  		/* Jump the left parenthesis '(' */
 19038  		pToken++; 
 19039  		/* Delimit the condition */
 19040  		jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 19041  		if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
 19042  			/* Syntax error */
 19043  			if( pToken >= pGen->pEnd ){
 19044  				pToken--;
 19045  			}
 19046  			rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
 19047  			if( rc == SXERR_ABORT ){
 19048  				/* Error count limit reached, abort immediately */
 19049  				return SXERR_ABORT;
 19050  			}
 19051  			goto Synchronize;
 19052  		}
 19053  		/* Swap token streams */
 19054  		SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
 19055  		/* Compile the condition */
 19056  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 19057  		/* Update token stream */
 19058  		while(pGen->pIn < pEnd ){
 19059  			jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
 19060  			pGen->pIn++;
 19061  		}
 19062  		pGen->pIn  = &pEnd[1];
 19063  		pGen->pEnd = pTmp;
 19064  		if( rc == SXERR_ABORT ){
 19065  			/* Expression handler request an operation abort [i.e: Out-of-memory] */
 19066  			return SXERR_ABORT;
 19067  		}
 19068  		/* Emit the false jump */
 19069  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
 19070  		/* Save the instruction index so we can fix it later when the jump destination is resolved */
 19071  		GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
 19072  		/* Compile the body */
 19073  		rc = jx9CompileBlock(&(*pGen));
 19074  		if( rc == SXERR_ABORT ){
 19075  			return SXERR_ABORT;
 19076  		}
 19077  		if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
 19078  			break;
 19079  		}
 19080  		/* Ensure that the keyword ID is 'else if' or 'else' */
 19081  		nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
 19082  		if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
 19083  			break;
 19084  		}
 19085  		/* Emit the unconditional jump */
 19086  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
 19087  		/* Save the instruction index so we can fix it later when the jump destination is resolved */
 19088  		GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
 19089  		if( nKeyID & JX9_TKWRD_ELSE ){
 19090  			pToken = &pGen->pIn[1];
 19091  			if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
 19092  				SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
 19093  					break;
 19094  			}
 19095  			pGen->pIn++; /* Jump the 'else' keyword */
 19096  		}
 19097  		pGen->pIn++; /* Jump the 'elseif/if' keyword */
 19098  		/* Synchronize cursors */
 19099  		pToken = pGen->pIn;
 19100  		/* Fix the false jump */
 19101  		GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
 19102  	} /* For(;;) */
 19103  	/* Fix the false jump */
 19104  	GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
 19105  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
 19106  		(SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
 19107  			/* Compile the else block */
 19108  			pGen->pIn++;
 19109  			rc = jx9CompileBlock(&(*pGen));
 19110  			if( rc == SXERR_ABORT ){
 19111  				
 19112  				return SXERR_ABORT;
 19113  			}
 19114  	}
 19115  	nJumpIdx = jx9VmInstrLength(pGen->pVm);
 19116  	/* Fix all unconditional jumps now the destination is resolved */
 19117  	GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
 19118  	/* Release the conditional block */
 19119  	GenStateLeaveBlock(pGen, 0);
 19120  	/* Statement successfully compiled */
 19121  	return SXRET_OK;
 19122  Synchronize:
 19123  	/* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
 19124  	 */
 19125  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
 19126  		pGen->pIn++;
 19127  	}
 19128  	return SXRET_OK;
 19129  }
 19130  /*
 19131   * Compile the return statement.
 19132   * According to the JX9 language reference
 19133   *  If called from within a function, the return() statement immediately ends execution
 19134   *  of the current function, and returns its argument as the value of the function call.
 19135   *  return() will also end the execution of an eval() statement or script file.
 19136   *  If called from the global scope, then execution of the current script file is ended.
 19137   *  If the current script file was include()ed or require()ed, then control is passed back
 19138   *  to the calling file. Furthermore, if the current script file was include()ed, then the value
 19139   *  given to return() will be returned as the value of the include() call. If return() is called
 19140   *  from within the main script file, then script execution end.
 19141   *  Note that since return() is a language construct and not a function, the parentheses
 19142   *  surrounding its arguments are not required. It is common to leave them out, and you actually
 19143   *  should do so as JX9 has less work to do in this case. 
 19144   *  Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
 19145   */
 19146  static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
 19147  {
 19148  	sxi32 nRet = 0; /* TRUE if there is a return value */
 19149  	sxi32 rc;
 19150  	/* Jump the 'return' keyword */
 19151  	pGen->pIn++;
 19152  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 19153  		/* Compile the expression */
 19154  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 19155  		if( rc == SXERR_ABORT ){
 19156  			return SXERR_ABORT;
 19157  		}else if(rc != SXERR_EMPTY ){
 19158  			nRet = 1;
 19159  		}
 19160  	}
 19161  	/* Emit the done instruction */
 19162  	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
 19163  	return SXRET_OK;
 19164  }
 19165  /*
 19166   * Compile the die/exit language construct.
 19167   * The role of these constructs is to terminate execution of the script.
 19168   * Shutdown functions will always be executed even if exit() is called.
 19169   */
 19170  static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
 19171  {
 19172  	sxi32 nExpr = 0;
 19173  	sxi32 rc;
 19174  	/* Jump the die/exit keyword */
 19175  	pGen->pIn++;
 19176  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 19177  		/* Compile the expression */
 19178  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 19179  		if( rc == SXERR_ABORT ){
 19180  			return SXERR_ABORT;
 19181  		}else if(rc != SXERR_EMPTY ){
 19182  			nExpr = 1;
 19183  		}
 19184  	}
 19185  	/* Emit the HALT instruction */
 19186  	jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
 19187  	return SXRET_OK;
 19188  }
 19189  /*
 19190   * Compile the static statement.
 19191   * According to the JX9 language reference
 19192   *  Another important feature of variable scoping is the static variable.
 19193   *  A static variable exists only in a local function scope, but it does not lose its value
 19194   *  when program execution leaves this scope.
 19195   *  Static variables also provide one way to deal with recursive functions.
 19196   */
 19197  static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
 19198  {
 19199  	jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
 19200  	jx9_vm_func *pFunc;             /* Enclosing function */
 19201  	GenBlock *pBlock;
 19202  	SyString *pName;
 19203  	char *zDup;
 19204  	sxu32 nLine;
 19205  	sxi32 rc;
 19206  	/* Jump the static keyword */
 19207  	nLine = pGen->pIn->nLine;
 19208  	pGen->pIn++;
 19209  	/* Extract the enclosing function if any */
 19210  	pBlock = pGen->pCurrent;
 19211  	while( pBlock ){
 19212  		if( pBlock->iFlags & GEN_BLOCK_FUNC){
 19213  			break;
 19214  		}
 19215  		/* Point to the upper block */
 19216  		pBlock = pBlock->pParent;
 19217  	}
 19218  	if( pBlock == 0 ){
 19219  		/* Static statement, called outside of a function body, treat it as a simple variable. */
 19220  		if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
 19221  			rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
 19222  			if( rc == SXERR_ABORT ){
 19223  				return SXERR_ABORT;
 19224  			}
 19225  			goto Synchronize;
 19226  		}
 19227  		/* Compile the expression holding the variable */
 19228  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 19229  		if( rc == SXERR_ABORT ){
 19230  			return SXERR_ABORT;
 19231  		}else if( rc != SXERR_EMPTY ){
 19232  			/* Emit the POP instruction */
 19233  			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
 19234  		}
 19235  		return SXRET_OK;
 19236  	}
 19237  	pFunc = (jx9_vm_func *)pBlock->pUserData;
 19238  	/* Make sure we are dealing with a valid statement */
 19239  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
 19240  		(pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
 19241  			rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
 19242  			if( rc == SXERR_ABORT ){
 19243  				return SXERR_ABORT;
 19244  			}
 19245  			goto Synchronize;
 19246  	}
 19247  	pGen->pIn++;
 19248  	/* Extract variable name */
 19249  	pName = &pGen->pIn->sData;
 19250  	pGen->pIn++; /* Jump the var name */
 19251  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
 19252  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
 19253  		goto Synchronize;
 19254  	}
 19255  	/* Initialize the structure describing the static variable */
 19256  	SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
 19257  	sStatic.nIdx = SXU32_HIGH; /* Not yet created */
 19258  	/* Duplicate variable name */
 19259  	zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
 19260  	if( zDup == 0 ){
 19261  		jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
 19262  		return SXERR_ABORT;
 19263  	}
 19264  	SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
 19265  	/* Check if we have an expression to compile */
 19266  	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
 19267  		SySet *pInstrContainer;
 19268  		pGen->pIn++; /* Jump the equal '=' sign */
 19269  		/* Swap bytecode container */
 19270  		pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
 19271  		jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
 19272  		/* Compile the expression */
 19273  		rc = jx9CompileExpr(&(*pGen), 0, 0);
 19274  		/* Emit the done instruction */
 19275  		jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
 19276  		/* Restore default bytecode container */
 19277  		jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
 19278  	}
 19279  	/* Finally save the compiled static variable in the appropriate container */
 19280  	SySetPut(&pFunc->aStatic, (const void *)&sStatic);
 19281  	return SXRET_OK;
 19282  Synchronize:
 19283  	/* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
 19284  	 * statement. 
 19285  	 */
 19286  	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ==  0 ){
 19287  		pGen->pIn++;
 19288  	}
 19289  	return SXRET_OK;
 19290  }
 19291  /*
 19292   * Compile the 'const' statement.
 19293   * According to the JX9 language reference
 19294   *  A constant is an identifier (name) for a simple value. As the name suggests, that value
 19295   *  cannot change during the execution of the script (except for magic constants, which aren't actually constants).
 19296   *  A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
 19297   *  The name of a constant follows the same rules as any label in JX9. A valid constant name starts
 19298   *  with a letter or underscore, followed by any number of letters, numbers, or underscores.
 19299   *  As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* 
 19300   *  Syntax
 19301   *  You can define a constant by using the define()-function or by using the const keyword outside
 19302   *  a object definition. Once a constant is defined, it can never be changed or undefined.
 19303   *  You can get the value of a constant by simply specifying its name. Unlike with variables
 19304   *  you should not prepend a constant with a $. You can also use the function constant() to read
 19305   *  a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
 19306   *  to get a list of all defined constants.
 19307   */
 19308  static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
 19309  {
 19310  	SySet *pConsCode, *pInstrContainer;
 19311  	sxu32 nLine = pGen->pIn->nLine;
 19312  	SyString *pName;
 19313  	sxi32 rc;
 19314  	pGen->pIn++; /* Jump the 'const' keyword */
 19315  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
 19316  		/* Invalid constant name */
 19317  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
 19318  		if( rc == SXERR_ABORT ){
 19319  			/* Error count limit reached, abort immediately */
 19320  			return SXERR_ABORT;
 19321  		}
 19322  		goto Synchronize;
 19323  	}
 19324  	/* Peek constant name */
 19325  	pName = &pGen->pIn->sData;
 19326  	/* Make sure the constant name isn't reserved */
 19327  	if( GenStateIsReservedID(pName) ){
 19328  		/* Reserved constant */
 19329  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
 19330  		if( rc == SXERR_ABORT ){
 19331  			/* Error count limit reached, abort immediately */
 19332  			return SXERR_ABORT;
 19333  		}
 19334  		goto Synchronize;
 19335  	}
 19336  	pGen->pIn++;
 19337  	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
 19338  		/* Invalid statement*/
 19339  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
 19340  		if( rc == SXERR_ABORT ){
 19341  			/* Error count limit reached, abort immediately */
 19342  			return SXERR_ABORT;
 19343  		}
 19344  		goto Synchronize;
 19345  	}
 19346  	pGen->pIn++; /*Jump the equal sign */
 19347  	/* Allocate a new constant value container */
 19348  	pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
 19349  	if( pConsCode == 0 ){
 19350  		return GenStateOutOfMem(pGen);
 19351  	}
 19352  	SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
 19353  	/* Swap bytecode container */
 19354  	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
 19355  	jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
 19356  	/* Compile constant value */
 19357  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 19358  	/* Emit the done instruction */
 19359  	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
 19360  	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
 19361  	if( rc == SXERR_ABORT ){
 19362  		/* Don't worry about freeing memory, everything will be released shortly */
 19363  		return SXERR_ABORT;
 19364  	}
 19365  	SySetSetUserData(pConsCode, pGen->pVm);
 19366  	/* Register the constant */
 19367  	rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
 19368  	if( rc != SXRET_OK ){
 19369  		SySetRelease(pConsCode);
 19370  		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
 19371  	}
 19372  	return SXRET_OK;
 19373  Synchronize:
 19374  	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
 19375  	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 19376  		pGen->pIn++;
 19377  	}
 19378  	return SXRET_OK;
 19379  }
 19380  /*
 19381   * Compile the uplink construct.
 19382   * According to the JX9 language reference
 19383   *  In JX9 global variables must be declared uplink inside a function if they are going
 19384   *  to be used in that function.
 19385   *  Example #1 Using global
 19386   *   $a = 1;
 19387   *   $b = 2;
 19388   *   function Sum()
 19389   *   {
 19390   *    uplink $a, $b;
 19391   *    $b = $a + $b;
 19392   *   } 
 19393   *   Sum();
 19394   *   print $b;
 19395   *  ?>
 19396   *  The above script will output 3. By declaring $a and $b global within the function
 19397   *  all references to either variable will refer to the global version. There is no limit
 19398   *  to the number of global variables that can be manipulated by a function.
 19399   */
 19400  static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
 19401  {
 19402  	SyToken *pTmp, *pNext = 0;
 19403  	sxi32 nExpr;
 19404  	sxi32 rc;
 19405  	/* Jump the 'uplink' keyword */
 19406  	pGen->pIn++;
 19407  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
 19408  		/* Nothing to process */
 19409  		return SXRET_OK;
 19410  	}
 19411  	pTmp = pGen->pEnd;
 19412  	nExpr = 0;
 19413  	while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
 19414  		if( pGen->pIn < pNext ){
 19415  			pGen->pEnd = pNext;
 19416  			if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
 19417  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
 19418  				if( rc == SXERR_ABORT ){
 19419  					return SXERR_ABORT;
 19420  				}
 19421  			}else{
 19422  				pGen->pIn++;
 19423  				if( pGen->pIn >= pGen->pEnd ){
 19424  					/* Emit a warning */
 19425  					jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
 19426  				}else{
 19427  					rc = jx9CompileExpr(&(*pGen), 0, 0);
 19428  					if( rc == SXERR_ABORT ){
 19429  						return SXERR_ABORT;
 19430  					}else if(rc != SXERR_EMPTY ){
 19431  						nExpr++;
 19432  					}
 19433  				}
 19434  			}
 19435  		}
 19436  		/* Next expression in the stream */
 19437  		pGen->pIn = pNext;
 19438  		/* Jump trailing commas */
 19439  		while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
 19440  			pGen->pIn++;
 19441  		}
 19442  	}
 19443  	/* Restore token stream */
 19444  	pGen->pEnd = pTmp;
 19445  	if( nExpr > 0 ){
 19446  		/* Emit the uplink instruction */
 19447  		jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
 19448  	}
 19449  	return SXRET_OK;
 19450  }
 19451  /*
 19452   * Compile a switch block.
 19453   *  (See block-comment below for more information)
 19454   */
 19455  static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
 19456  {
 19457  	sxi32 rc = SXRET_OK;
 19458  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
 19459  		/* Unexpected token */
 19460  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
 19461  		if( rc == SXERR_ABORT ){
 19462  			return SXERR_ABORT;
 19463  		}
 19464  		pGen->pIn++;
 19465  	}
 19466  	pGen->pIn++;
 19467  	/* First instruction to execute in this block. */
 19468  	*pBlockStart = jx9VmInstrLength(pGen->pVm);
 19469  	/* Compile the block until we hit a case/default/endswitch keyword
 19470  	 * or the '}' token */
 19471  	for(;;){
 19472  		if( pGen->pIn >= pGen->pEnd ){
 19473  			/* No more input to process */
 19474  			break;
 19475  		}
 19476  		rc = SXRET_OK;
 19477  		if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
 19478  			if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
 19479  				rc = SXERR_EOF;
 19480  				break;
 19481  			}
 19482  		}else{
 19483  			sxi32 nKwrd;
 19484  			/* Extract the keyword */
 19485  			nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
 19486  			if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
 19487  				break;
 19488  			}
 19489  		}
 19490  		/* Compile block */
 19491  		rc = jx9CompileBlock(&(*pGen));
 19492  		if( rc == SXERR_ABORT ){
 19493  			return SXERR_ABORT;
 19494  		}
 19495  	}
 19496  	return rc;
 19497  }
 19498  /*
 19499   * Compile a case eXpression.
 19500   *  (See block-comment below for more information)
 19501   */
 19502  static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
 19503  {
 19504  	SySet *pInstrContainer;
 19505  	SyToken *pEnd, *pTmp;
 19506  	sxi32 iNest = 0;
 19507  	sxi32 rc;
 19508  	/* Delimit the expression */
 19509  	pEnd = pGen->pIn;
 19510  	while( pEnd < pGen->pEnd ){
 19511  		if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
 19512  			/* Increment nesting level */
 19513  			iNest++;
 19514  		}else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
 19515  			/* Decrement nesting level */
 19516  			iNest--;
 19517  		}else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
 19518  			break;
 19519  		}
 19520  		pEnd++;
 19521  	}
 19522  	if( pGen->pIn >= pEnd ){
 19523  		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
 19524  		if( rc == SXERR_ABORT ){
 19525  			/* Error count limit reached, abort immediately */
 19526  			return SXERR_ABORT;
 19527  		}
 19528  	}
 19529  	/* Swap token stream */
 19530  	pTmp = pGen->pEnd;
 19531  	pGen->pEnd = pEnd;
 19532  	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
 19533  	jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
 19534  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 19535  	/* Emit the done instruction */
 19536  	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
 19537  	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
 19538  	/* Update token stream */
 19539  	pGen->pIn  = pEnd;
 19540  	pGen->pEnd = pTmp;
 19541  	if( rc == SXERR_ABORT ){
 19542  		return SXERR_ABORT;
 19543  	}
 19544  	return SXRET_OK;
 19545  }
 19546  /*
 19547   * Compile the smart switch statement.
 19548   * According to the JX9 language reference manual
 19549   *  The switch statement is similar to a series of IF statements on the same expression.
 19550   *  In many occasions, you may want to compare the same variable (or expression) with many
 19551   *  different values, and execute a different piece of code depending on which value it equals to.
 19552   *  This is exactly what the switch statement is for.
 19553   *  Note: Note that unlike some other languages, the continue statement applies to switch and acts
 19554   *  similar to break. If you have a switch inside a loop and wish to continue to the next iteration
 19555   *  of the outer loop, use continue 2. 
 19556   *  Note that switch/case does loose comparision. 
 19557   *  It is important to understand how the switch statement is executed in order to avoid mistakes.
 19558   *  The switch statement executes line by line (actually, statement by statement).
 19559   *  In the beginning, no code is executed. Only when a case statement is found with a value that
 19560   *  matches the value of the switch expression does JX9 begin to execute the statements.
 19561   *  JX9 continues to execute the statements until the end of the switch block, or the first time
 19562   *  it sees a break statement. If you don't write a break statement at the end of a case's statement list.
 19563   *  In a switch statement, the condition is evaluated only once and the result is compared to each
 19564   *  case statement. In an elseif statement, the condition is evaluated again. If your condition
 19565   *  is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
 19566   *  The statement list for a case can also be empty, which simply passes control into the statement
 19567   *  list for the next case. 
 19568   *  The case expression may be any expression that evaluates to a simple type, that is, integer
 19569   *  or floating-point numbers and strings.
 19570   */
 19571  static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
 19572  {
 19573  	GenBlock *pSwitchBlock;
 19574  	SyToken *pTmp, *pEnd;
 19575  	jx9_switch *pSwitch;
 19576  	sxu32 nLine;
 19577  	sxi32 rc;
 19578  	nLine = pGen->pIn->nLine;
 19579  	/* Jump the 'switch' keyword */
 19580  	pGen->pIn++;    
 19581  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
 19582  		/* Syntax error */
 19583  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
 19584  		if( rc == SXERR_ABORT ){
 19585  			/* Error count limit reached, abort immediately */
 19586  			return SXERR_ABORT;
 19587  		}
 19588  		goto Synchronize;
 19589  	}
 19590  	/* Jump the left parenthesis '(' */
 19591  	pGen->pIn++; 
 19592  	pEnd = 0; /* cc warning */
 19593  	/* Create the loop block */
 19594  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH, 
 19595  		jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
 19596  	if( rc != SXRET_OK ){
 19597  		return SXERR_ABORT;
 19598  	}
 19599  	/* Delimit the condition */
 19600  	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 19601  	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
 19602  		/* Empty expression */
 19603  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
 19604  		if( rc == SXERR_ABORT ){
 19605  			/* Error count limit reached, abort immediately */
 19606  			return SXERR_ABORT;
 19607  		}
 19608  	}
 19609  	/* Swap token streams */
 19610  	pTmp = pGen->pEnd;
 19611  	pGen->pEnd = pEnd;
 19612  	/* Compile the expression */
 19613  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 19614  	if( rc == SXERR_ABORT ){
 19615  		/* Expression handler request an operation abort [i.e: Out-of-memory] */
 19616  		return SXERR_ABORT;
 19617  	}
 19618  	/* Update token stream */
 19619  	while(pGen->pIn < pEnd ){
 19620  		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, 
 19621  			"Switch: Unexpected token '%z'", &pGen->pIn->sData);
 19622  		if( rc == SXERR_ABORT ){
 19623  			return SXERR_ABORT;
 19624  		}
 19625  		pGen->pIn++;
 19626  	}
 19627  	pGen->pIn  = &pEnd[1];
 19628  	pGen->pEnd = pTmp;
 19629  	if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
 19630  		(pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
 19631  			pTmp = pGen->pIn;
 19632  			if( pTmp >= pGen->pEnd ){
 19633  				pTmp--;
 19634  			}
 19635  			/* Unexpected token */
 19636  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
 19637  			if( rc == SXERR_ABORT ){
 19638  				return SXERR_ABORT;
 19639  			}
 19640  			goto Synchronize;
 19641  	}
 19642  	pGen->pIn++; /* Jump the leading curly braces/colons */
 19643  	/* Create the switch blocks container */
 19644  	pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
 19645  	if( pSwitch == 0 ){
 19646  		/* Abort compilation */
 19647  		return GenStateOutOfMem(pGen);
 19648  	}
 19649  	/* Zero the structure */
 19650  	SyZero(pSwitch, sizeof(jx9_switch));
 19651  	/* Initialize fields */
 19652  	SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
 19653  	/* Emit the switch instruction */
 19654  	jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
 19655  	/* Compile case blocks */
 19656  	for(;;){
 19657  		sxu32 nKwrd;
 19658  		if( pGen->pIn >= pGen->pEnd ){
 19659  			/* No more input to process */
 19660  			break;
 19661  		}
 19662  		if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
 19663  			if(  (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
 19664  				/* Unexpected token */
 19665  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", 
 19666  					&pGen->pIn->sData);
 19667  				if( rc == SXERR_ABORT ){
 19668  					return SXERR_ABORT;
 19669  				}
 19670  				/* FALL THROUGH */
 19671  			}
 19672  			/* Block compiled */
 19673  			break;
 19674  		}
 19675  		/* Extract the keyword */
 19676  		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
 19677  		if( nKwrd == JX9_TKWRD_DEFAULT ){
 19678  			/*
 19679  			 * Accroding to the JX9 language reference manual
 19680  			 *  A special case is the default case. This case matches anything
 19681  			 *  that wasn't matched by the other cases.
 19682  			 */
 19683  			if( pSwitch->nDefault > 0 ){
 19684  				/* Default case already compiled */ 
 19685  				rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
 19686  				if( rc == SXERR_ABORT ){
 19687  					return SXERR_ABORT;
 19688  				}
 19689  			}
 19690  			pGen->pIn++; /* Jump the 'default' keyword */
 19691  			/* Compile the default block */
 19692  			rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
 19693  			if( rc == SXERR_ABORT){
 19694  				return SXERR_ABORT;
 19695  			}else if( rc == SXERR_EOF ){
 19696  				break;
 19697  			}
 19698  		}else if( nKwrd == JX9_TKWRD_CASE ){
 19699  			jx9_case_expr sCase;
 19700  			/* Standard case block */
 19701  			pGen->pIn++; /* Jump the 'case' keyword */
 19702  			/* initialize the structure */
 19703  			SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
 19704  			/* Compile the case expression */
 19705  			rc = GenStateCompileCaseExpr(pGen, &sCase);
 19706  			if( rc == SXERR_ABORT ){
 19707  				return SXERR_ABORT;
 19708  			}
 19709  			/* Compile the case block */
 19710  			rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
 19711  			/* Insert in the switch container */
 19712  			SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
 19713  			if( rc == SXERR_ABORT){
 19714  				return SXERR_ABORT;
 19715  			}else if( rc == SXERR_EOF ){
 19716  				break;
 19717  			}
 19718  		}else{
 19719  			/* Unexpected token */
 19720  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", 
 19721  				&pGen->pIn->sData);
 19722  			if( rc == SXERR_ABORT ){
 19723  				return SXERR_ABORT;
 19724  			}
 19725  			break;
 19726  		}
 19727  	}
 19728  	/* Fix all jumps now the destination is resolved */
 19729  	pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
 19730  	GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
 19731  	/* Release the loop block */
 19732  	GenStateLeaveBlock(pGen, 0);
 19733  	if( pGen->pIn < pGen->pEnd ){
 19734  		/* Jump the trailing curly braces */
 19735  		pGen->pIn++;
 19736  	}
 19737  	/* Statement successfully compiled */
 19738  	return SXRET_OK;
 19739  Synchronize:
 19740  	/* Synchronize with the first semi-colon */
 19741  	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
 19742  		pGen->pIn++;
 19743  	}
 19744  	return SXRET_OK;
 19745  }
 19746  /*
 19747   * Process default argument values. That is, a function may define C++-style default value
 19748   * as follows:
 19749   * function makecoffee($type = "cappuccino")
 19750   * {
 19751   *   return "Making a cup of $type.\n";
 19752   * }
 19753   * Some features:
 19754   *  1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
 19755   *      functions, array member, ..]
 19756   * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
 19757   *      Example:
 19758   *           function a(string $a){} function b(int $a, string $c, float $d){}
 19759   * 3 -) Function overloading!!
 19760   *      Example:
 19761   *      function foo($a) {
 19762   *   	  return $a.JX9_EOL;
 19763   *	    }
 19764   *	    function foo($a, $b) {
 19765   *   	  return $a + $b;
 19766   *	    }
 19767   *	    print foo(5); // Prints "5"
 19768   *	    print foo(5, 2); // Prints "7"
 19769   *      // Same arg
 19770   *	   function foo(string $a)
 19771   *	   {
 19772   *	     print "a is a string\n";
 19773   *	     dump($a);
 19774   *	   }
 19775   *	  function foo(int $a)
 19776   *	  {
 19777   *	    print "a is integer\n";
 19778   *	    dump($a);
 19779   *	  }
 19780   *	  function foo(array $a)
 19781   *	  {
 19782   * 	    print "a is an array\n";
 19783   * 	    dump($a);
 19784   *	  }
 19785   *	  foo('This is a great feature'); // a is a string [first foo]
 19786   *	  foo(52); // a is integer [second foo] 
 19787   *    foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
 19788   * Please refer to the official documentation for more information on the powerful extension
 19789   * introduced by the JX9 engine.
 19790   */
 19791  static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
 19792  {
 19793  	SyToken *pTmpIn, *pTmpEnd;
 19794  	SySet *pInstrContainer;
 19795  	sxi32 rc;
 19796  	/* Swap token stream */
 19797  	SWAP_DELIMITER(pGen, pIn, pEnd);
 19798  	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
 19799  	jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
 19800  	/* Compile the expression holding the argument value */
 19801  	rc = jx9CompileExpr(&(*pGen), 0, 0);
 19802  	/* Emit the done instruction */
 19803  	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
 19804  	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
 19805  	RE_SWAP_DELIMITER(pGen);
 19806  	if( rc == SXERR_ABORT ){
 19807  		return SXERR_ABORT;
 19808  	}
 19809  	return SXRET_OK;
 19810  }
 19811  /*
 19812   * Collect function arguments one after one.
 19813   * According to the JX9 language reference manual.
 19814   * Information may be passed to functions via the argument list, which is a comma-delimited
 19815   * list of expressions.
 19816   * JX9 supports passing arguments by value (the default), passing by reference
 19817   * and default argument values. Variable-length argument lists are also supported, 
 19818   * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
 19819   * for more information.
 19820   * Example #1 Passing arrays to functions
 19821   * <?jx9
 19822   * function takes_array($input)
 19823   * {
 19824   *    print "$input[0] + $input[1] = ", $input[0]+$input[1];
 19825   * }
 19826   * ?>
 19827   * Making arguments be passed by reference
 19828   * By default, function arguments are passed by value (so that if the value of the argument
 19829   * within the function is changed, it does not get changed outside of the function).
 19830   * To allow a function to modify its arguments, they must be passed by reference.
 19831   * To have an argument to a function always passed by reference, prepend an ampersand (&)
 19832   * to the argument name in the function definition:
 19833   * Example #2 Passing function parameters by reference
 19834   * <?jx9
 19835   * function add_some_extra(&$string)
 19836   * {
 19837   *   $string .= 'and something extra.';
 19838   * }
 19839   * $str = 'This is a string, ';
 19840   * add_some_extra($str);
 19841   * print $str;    // outputs 'This is a string, and something extra.'
 19842   * ?>
 19843   *
 19844   * JX9 have introduced powerful extension including full type hinting, function overloading
 19845   * complex agrument values.Please refer to the official documentation for more information
 19846   * on these extension.
 19847   */
 19848  static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
 19849  {
 19850  	jx9_vm_func_arg sArg; /* Current processed argument */
 19851  	SyToken *pCur, *pIn;  /* Token stream */
 19852  	SyBlob sSig;         /* Function signature */
 19853  	char *zDup;          /* Copy of argument name */
 19854  	sxi32 rc;
 19855  
 19856  	pIn = pGen->pIn;
 19857  	pCur = 0;
 19858  	SyBlobInit(&sSig, &pGen->pVm->sAllocator);
 19859  	/* Process arguments one after one */
 19860  	for(;;){
 19861  		if( pIn >= pEnd ){
 19862  			/* No more arguments to process */
 19863  			break;
 19864  		}
 19865  		SyZero(&sArg, sizeof(jx9_vm_func_arg));
 19866  		SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
 19867  		if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
 19868  			if( pIn->nType & JX9_TK_KEYWORD ){
 19869  				sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
 19870  				if( nKey & JX9_TKWRD_BOOL ){
 19871  					sArg.nType = MEMOBJ_BOOL;
 19872  				}else if( nKey & JX9_TKWRD_INT ){
 19873  					sArg.nType = MEMOBJ_INT;
 19874  				}else if( nKey & JX9_TKWRD_STRING ){
 19875  					sArg.nType = MEMOBJ_STRING;
 19876  				}else if( nKey & JX9_TKWRD_FLOAT ){
 19877  					sArg.nType = MEMOBJ_REAL;
 19878  				}else{
 19879  					jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, 
 19880  						"Invalid argument type '%z', Automatic cast will not be performed", 
 19881  						&pIn->sData);
 19882  				}
 19883  			}
 19884  			pIn++;
 19885  		}
 19886  		if( pIn >= pEnd ){
 19887  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
 19888  			return rc;
 19889  		}
 19890  		if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
 19891  			/* Invalid argument */ 
 19892  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
 19893  			return rc;
 19894  		}
 19895  		pIn++; /* Jump the dollar sign */
 19896  		/* Copy argument name */
 19897  		zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
 19898  		if( zDup == 0 ){
 19899  			return GenStateOutOfMem(pGen);
 19900  		}
 19901  		SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
 19902  		pIn++;
 19903  		if( pIn < pEnd ){
 19904  			if( pIn->nType & JX9_TK_EQUAL ){
 19905  				SyToken *pDefend;
 19906  				sxi32 iNest = 0;
 19907  				pIn++; /* Jump the equal sign */
 19908  				pDefend = pIn;
 19909  				/* Process the default value associated with this argument */
 19910  				while( pDefend < pEnd ){
 19911  					if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
 19912  						break;
 19913  					}
 19914  					if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
 19915  						/* Increment nesting level */
 19916  						iNest++;
 19917  					}else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
 19918  						/* Decrement nesting level */
 19919  						iNest--;
 19920  					}
 19921  					pDefend++;
 19922  				}
 19923  				if( pIn >= pDefend ){
 19924  					rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
 19925  					return rc;
 19926  				}
 19927  				/* Process default value */
 19928  				rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
 19929  				if( rc != SXRET_OK ){
 19930  					return rc;
 19931  				}
 19932  				/* Point beyond the default value */
 19933  				pIn = pDefend;
 19934  			}
 19935  			if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
 19936  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
 19937  				return rc;
 19938  			}
 19939  			pIn++; /* Jump the trailing comma */
 19940  		}
 19941  		/* Append argument signature */
 19942  		if( sArg.nType > 0 ){
 19943  			int c;
 19944  			c = 'n'; /* cc warning */
 19945  			/* Type leading character */
 19946  			switch(sArg.nType){
 19947  				case MEMOBJ_HASHMAP:
 19948  					/* Hashmap aka 'array' */
 19949  					c = 'h';
 19950  					break;
 19951  				case MEMOBJ_INT:
 19952  					/* Integer */
 19953  					c = 'i';
 19954  					break;
 19955  				case MEMOBJ_BOOL:
 19956  					/* Bool */
 19957  					c = 'b';
 19958  					break;
 19959  				case MEMOBJ_REAL:
 19960  					/* Float */
 19961  					c = 'f';
 19962  					break;
 19963  				case MEMOBJ_STRING:
 19964  					/* String */
 19965  					c = 's';
 19966  					break;
 19967  				default:
 19968  					break;
 19969  				}
 19970  				SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
 19971  		}
 19972  		/* Save in the argument set */
 19973  		SySetPut(&pFunc->aArgs, (const void *)&sArg);
 19974  	}
 19975  	if( SyBlobLength(&sSig) > 0 ){
 19976  		/* Save function signature */
 19977  		SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
 19978  	}
 19979  	return SXRET_OK;
 19980  }
 19981  /*
 19982   * Compile function [i.e: standard function, annonymous function or closure ] body.
 19983   * Return SXRET_OK on success. Any other return value indicates failure
 19984   * and this routine takes care of generating the appropriate error message.
 19985   */
 19986  static sxi32 GenStateCompileFuncBody(
 19987  	jx9_gen_state *pGen,  /* Code generator state */
 19988  	jx9_vm_func *pFunc    /* Function state */
 19989  	)
 19990  {
 19991  	SySet *pInstrContainer; /* Instruction container */
 19992  	GenBlock *pBlock;
 19993  	sxi32 rc;
 19994  	/* Attach the new function */
 19995  	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
 19996  	if( rc != SXRET_OK ){
 19997  		return GenStateOutOfMem(pGen);
 19998  	}
 19999  	/* Swap bytecode containers */
 20000  	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
 20001  	jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
 20002  	/* Compile the body */
 20003  	jx9CompileBlock(&(*pGen));
 20004  	/* Emit the final return if not yet done */
 20005  	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
 20006  	/* Restore the default container */
 20007  	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
 20008  	/* Leave function block */
 20009  	GenStateLeaveBlock(&(*pGen), 0);
 20010  	if( rc == SXERR_ABORT ){
 20011  		/* Don't worry about freeing memory, everything will be released shortly */
 20012  		return SXERR_ABORT;
 20013  	}
 20014  	/* All done, function body compiled */
 20015  	return SXRET_OK;
 20016  }
 20017  /*
 20018   * Compile a JX9 function whether is a Standard or Annonymous function.
 20019   * According to the JX9 language reference manual.
 20020   *  Function names follow the same rules as other labels in JX9. A valid function name
 20021   *  starts with a letter or underscore, followed by any number of letters, numbers, or
 20022   *  underscores. As a regular expression, it would be expressed thus:
 20023   *     [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*. 
 20024   *  Functions need not be defined before they are referenced.
 20025   *  All functions and objectes in JX9 have the global scope - they can be called outside
 20026   *  a function even if they were defined inside and vice versa.
 20027   *  It is possible to call recursive functions in JX9. However avoid recursive function/method
 20028   *  calls with over 32-64 recursion levels. 
 20029   * 
 20030   * JX9 have introduced powerful extension including full type hinting, function overloading, 
 20031   * complex agrument values and more. Please refer to the official documentation for more information
 20032   * on these extension.
 20033   */
 20034  static sxi32 GenStateCompileFunc(
 20035  	jx9_gen_state *pGen, /* Code generator state */
 20036  	SyString *pName,     /* Function name. NULL otherwise */
 20037  	sxi32 iFlags,        /* Control flags */
 20038  	jx9_vm_func **ppFunc /* OUT: function state */
 20039  	)
 20040  {
 20041  	jx9_vm_func *pFunc;
 20042  	SyToken *pEnd;
 20043  	sxu32 nLine;
 20044  	char *zName;
 20045  	sxi32 rc;
 20046  	/* Extract line number */
 20047  	nLine = pGen->pIn->nLine;
 20048  	/* Jump the left parenthesis '(' */
 20049  	pGen->pIn++;
 20050  	/* Delimit the function signature */
 20051  	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
 20052  	if( pEnd >= pGen->pEnd ){
 20053  		/* Syntax error */
 20054  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
 20055  		if( rc == SXERR_ABORT ){
 20056  			/* Error count limit reached, abort immediately */
 20057  			return SXERR_ABORT;
 20058  		}
 20059  		pGen->pIn = pGen->pEnd;
 20060  		return SXRET_OK;
 20061  	}
 20062  	/* Create the function state */
 20063  	pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
 20064  	if( pFunc == 0 ){
 20065  		goto OutOfMem;
 20066  	}
 20067  	/* function ID */
 20068  	zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
 20069  	if( zName == 0 ){
 20070  		/* Don't worry about freeing memory, everything will be released shortly */
 20071  		goto OutOfMem;
 20072  	}
 20073  	/* Initialize the function state */
 20074  	jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
 20075  	if( pGen->pIn < pEnd ){
 20076  		/* Collect function arguments */
 20077  		rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
 20078  		if( rc == SXERR_ABORT ){
 20079  			/* Don't worry about freeing memory, everything will be released shortly */
 20080  			return SXERR_ABORT;
 20081  		}
 20082  	}
 20083  	/* Compile function body */
 20084  	pGen->pIn = &pEnd[1];
 20085  	/* Compile the body */
 20086  	rc = GenStateCompileFuncBody(&(*pGen), pFunc);
 20087  	if( rc == SXERR_ABORT ){
 20088  		return SXERR_ABORT;
 20089  	}
 20090  	if( ppFunc ){
 20091  		*ppFunc = pFunc;
 20092  	}
 20093  	/* Finally register the function */
 20094  	rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
 20095  	return rc;
 20096  	/* Fall through if something goes wrong */
 20097  OutOfMem:
 20098  	/* If the supplied memory subsystem is so sick that we are unable to allocate
 20099  	 * a tiny chunk of memory, there is no much we can do here.
 20100  	 */
 20101  	return GenStateOutOfMem(pGen);
 20102  }
 20103  /*
 20104   * Compile a standard JX9 function.
 20105   *  Refer to the block-comment above for more information.
 20106   */
 20107  static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
 20108  {
 20109  	SyString *pName;
 20110  	sxi32 iFlags;
 20111  	sxu32 nLine;
 20112  	sxi32 rc;
 20113  
 20114  	nLine = pGen->pIn->nLine;
 20115  	pGen->pIn++; /* Jump the 'function' keyword */
 20116  	iFlags = 0;
 20117  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
 20118  		/* Invalid function name */
 20119  		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
 20120  		if( rc == SXERR_ABORT ){
 20121  			return SXERR_ABORT;
 20122  		}
 20123  		/* Sychronize with the next semi-colon or braces*/
 20124  		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
 20125  			pGen->pIn++;
 20126  		}
 20127  		return SXRET_OK;
 20128  	}
 20129  	pName = &pGen->pIn->sData;
 20130  	nLine = pGen->pIn->nLine;
 20131  	/* Jump the function name */
 20132  	pGen->pIn++;
 20133  	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
 20134  		/* Syntax error */
 20135  		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
 20136  		if( rc == SXERR_ABORT ){
 20137  			/* Error count limit reached, abort immediately */
 20138  			return SXERR_ABORT;
 20139  		}
 20140  		/* Sychronize with the next semi-colon or '{' */
 20141  		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
 20142  			pGen->pIn++;
 20143  		}
 20144  		return SXRET_OK;
 20145  	}
 20146  	/* Compile function body */
 20147  	rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
 20148  	return rc;
 20149  }
 20150  /*
 20151   * Generate bytecode for a given expression tree.
 20152   * If something goes wrong while generating bytecode
 20153   * for the expression tree (A very unlikely scenario)
 20154   * this function takes care of generating the appropriate
 20155   * error message.
 20156   */
 20157  static sxi32 GenStateEmitExprCode(
 20158  	jx9_gen_state *pGen,  /* Code generator state */
 20159  	jx9_expr_node *pNode, /* Root of the expression tree */
 20160  	sxi32 iFlags /* Control flags */
 20161  	)
 20162  {
 20163  	VmInstr *pInstr;
 20164  	sxu32 nJmpIdx;
 20165  	sxi32 iP1 = 0;
 20166  	sxu32 iP2 = 0;
 20167  	void *p3  = 0;
 20168  	sxi32 iVmOp;
 20169  	sxi32 rc;
 20170  	if( pNode->xCode ){
 20171  		SyToken *pTmpIn, *pTmpEnd;
 20172  		/* Compile node */
 20173  		SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
 20174  		rc = pNode->xCode(&(*pGen), iFlags);
 20175  		RE_SWAP_DELIMITER(pGen);
 20176  		return rc;
 20177  	}
 20178  	if( pNode->pOp == 0 ){
 20179  		jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine, 
 20180  			"Invalid expression node, JX9 is aborting compilation");
 20181  		return SXERR_ABORT;
 20182  	}
 20183  	iVmOp = pNode->pOp->iVmOp;
 20184  	if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
 20185  		sxu32 nJz, nJmp;
 20186  		/* Ternary operator require special handling */
 20187  		/* Phase#1: Compile the condition */
 20188  		rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
 20189  		if( rc != SXRET_OK ){
 20190  			return rc;
 20191  		}
 20192  		nJz = nJmp = 0; /* cc -O6 warning */
 20193  		/* Phase#2: Emit the false jump */
 20194  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
 20195  		if( pNode->pLeft ){
 20196  			/* Phase#3: Compile the 'then' expression  */
 20197  			rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
 20198  			if( rc != SXRET_OK ){
 20199  				return rc;
 20200  			}
 20201  		}
 20202  		/* Phase#4: Emit the unconditional jump */
 20203  		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
 20204  		/* Phase#5: Fix the false jump now the jump destination is resolved. */
 20205  		pInstr = jx9VmGetInstr(pGen->pVm, nJz);
 20206  		if( pInstr ){
 20207  			pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
 20208  		}
 20209  		/* Phase#6: Compile the 'else' expression */
 20210  		if( pNode->pRight ){
 20211  			rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
 20212  			if( rc != SXRET_OK ){
 20213  				return rc;
 20214  			}
 20215  		}
 20216  		if( nJmp > 0 ){
 20217  			/* Phase#7: Fix the unconditional jump */
 20218  			pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
 20219  			if( pInstr ){
 20220  				pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
 20221  			}
 20222  		}
 20223  		/* All done */
 20224  		return SXRET_OK;
 20225  	}
 20226  	/* Generate code for the left tree */
 20227  	if( pNode->pLeft ){
 20228  		if( iVmOp == JX9_OP_CALL ){
 20229  			jx9_expr_node **apNode;
 20230  			sxi32 n;
 20231  			/* Recurse and generate bytecodes for function arguments */
 20232  			apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
 20233  			/* Read-only load */
 20234  			iFlags |= EXPR_FLAG_RDONLY_LOAD;
 20235  			for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
 20236  				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
 20237  				if( rc != SXRET_OK ){
 20238  					return rc;
 20239  				}
 20240  			}
 20241  			/* Total number of given arguments */
 20242  			iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
 20243  			/* Remove stale flags now */
 20244  			iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
 20245  		}
 20246  		rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
 20247  		if( rc != SXRET_OK ){
 20248  			return rc;
 20249  		}
 20250  		if( iVmOp == JX9_OP_CALL ){
 20251  			pInstr = jx9VmPeekInstr(pGen->pVm);
 20252  			if( pInstr ){
 20253  				if ( pInstr->iOp == JX9_OP_LOADC ){
 20254  					/* Prevent constant expansion */
 20255  					pInstr->iP1 = 0;
 20256  				}else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */  ){
 20257  					/* Annonymous function call, flag that */
 20258  					pInstr->iP2 = 1;
 20259  				}
 20260  			}
 20261  		}else if( iVmOp == JX9_OP_LOAD_IDX ){
 20262  			jx9_expr_node **apNode;
 20263  			sxi32 n;
 20264  			/* Recurse and generate bytecodes for array index */
 20265  			apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
 20266  			for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
 20267  				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
 20268  				if( rc != SXRET_OK ){
 20269  					return rc;
 20270  				}
 20271  			}
 20272  			if( SySetUsed(&pNode->aNodeArgs) > 0 ){
 20273  				iP1 = 1; /* Node have an index associated with it */
 20274  			}
 20275  			if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
 20276  				/* Create an empty entry when the desired index is not found */
 20277  				iP2 = 1;
 20278  			}
 20279  		}else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
 20280  			/* POP the left node */
 20281  			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
 20282  		}
 20283  	}
 20284  	rc = SXRET_OK;
 20285  	nJmpIdx = 0;
 20286  	/* Generate code for the right tree */
 20287  	if( pNode->pRight ){
 20288  		if( iVmOp == JX9_OP_LAND ){
 20289  			/* Emit the false jump so we can short-circuit the logical and */
 20290  			jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
 20291  		}else if (iVmOp == JX9_OP_LOR ){
 20292  			/* Emit the true jump so we can short-circuit the logical or*/
 20293  			jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
 20294  		}else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
 20295  			iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
 20296  		}
 20297  		rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
 20298  		if( iVmOp == JX9_OP_STORE ){
 20299  			pInstr = jx9VmPeekInstr(pGen->pVm);
 20300  			if( pInstr ){
 20301  				if(pInstr->iOp == JX9_OP_MEMBER ){
 20302  					/* Perform a member store operation [i.e: $this.x = 50] */
 20303  					iP2 = 1;
 20304  				}else{
 20305  					if( pInstr->iOp == JX9_OP_LOAD_IDX ){
 20306  						/* Transform the STORE instruction to STORE_IDX instruction */
 20307  						iVmOp = JX9_OP_STORE_IDX;
 20308  						iP1 = pInstr->iP1;
 20309  					}else{
 20310  						p3 = pInstr->p3;
 20311  					}
 20312  					/* POP the last dynamic load instruction */
 20313  					(void)jx9VmPopInstr(pGen->pVm);
 20314  				}
 20315  			}
 20316  		}
 20317  	}
 20318  	if( iVmOp > 0 ){
 20319  		if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
 20320  			if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
 20321  				/* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
 20322  				iP1 = 1;
 20323  			}
 20324  		}
 20325  		/* Finally, emit the VM instruction associated with this operator */
 20326  		jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
 20327  		if( nJmpIdx > 0 ){
 20328  			/* Fix short-circuited jumps now the destination is resolved */
 20329  			pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
 20330  			if( pInstr ){
 20331  				pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
 20332  			}
 20333  		}
 20334  	}
 20335  	return rc;
 20336  }
 20337  /*
 20338   * Compile a JX9 expression.
 20339   * According to the JX9 language reference manual:
 20340   *  Expressions are the most important building stones of JX9.
 20341   *  In JX9, almost anything you write is an expression.
 20342   *  The simplest yet most accurate way to define an expression
 20343   *  is "anything that has a value". 
 20344   * If something goes wrong while compiling the expression, this
 20345   * function takes care of generating the appropriate error
 20346   * message.
 20347   */
 20348  static sxi32 jx9CompileExpr(
 20349  	jx9_gen_state *pGen, /* Code generator state */
 20350  	sxi32 iFlags,        /* Control flags */
 20351  	sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
 20352  	)
 20353  {
 20354  	jx9_expr_node *pRoot;
 20355  	SySet sExprNode;
 20356  	SyToken *pEnd;
 20357  	sxi32 nExpr;
 20358  	sxi32 iNest;
 20359  	sxi32 rc;
 20360  	/* Initialize worker variables */
 20361  	nExpr = 0;
 20362  	pRoot = 0;
 20363  	SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
 20364  	SySetAlloc(&sExprNode, 0x10);
 20365  	rc = SXRET_OK;
 20366  	/* Delimit the expression */
 20367  	pEnd = pGen->pIn;
 20368  	iNest = 0;
 20369  	while( pEnd < pGen->pEnd ){
 20370  		if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
 20371  			/* Ticket 1433-30: Annonymous/Closure functions body */
 20372  			iNest++;
 20373  		}else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
 20374  			iNest--;
 20375  		}else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
 20376  			if( iNest <= 0 ){
 20377  				break;
 20378  			}
 20379  		}
 20380  		pEnd++;
 20381  	}
 20382  	if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
 20383  		SyToken *pEnd2 = pGen->pIn;
 20384  		iNest = 0;
 20385  		/* Stop at the first comma */
 20386  		while( pEnd2 < pEnd ){
 20387  			if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
 20388  				iNest++;
 20389  			}else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
 20390  				iNest--;
 20391  			}else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
 20392  				if( iNest <= 0 ){
 20393  					break;
 20394  				}
 20395  			}
 20396  			pEnd2++;
 20397  		}
 20398  		if( pEnd2 <pEnd ){
 20399  			pEnd = pEnd2;
 20400  		}
 20401  	}
 20402  	if( pEnd > pGen->pIn ){
 20403  		SyToken *pTmp = pGen->pEnd;
 20404  		/* Swap delimiter */
 20405  		pGen->pEnd = pEnd;
 20406  		/* Try to get an expression tree */
 20407  		rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
 20408  		if( rc == SXRET_OK && pRoot ){
 20409  			rc = SXRET_OK;
 20410  			if( xTreeValidator ){
 20411  				/* Call the upper layer validator callback */
 20412  				rc = xTreeValidator(&(*pGen), pRoot);
 20413  			}
 20414  			if( rc != SXERR_ABORT ){
 20415  				/* Generate code for the given tree */
 20416  				rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
 20417  			}
 20418  			nExpr = 1;
 20419  		}
 20420  		/* Release the whole tree */
 20421  		jx9ExprFreeTree(&(*pGen), &sExprNode);
 20422  		/* Synchronize token stream */
 20423  		pGen->pEnd = pTmp;
 20424  		pGen->pIn  = pEnd;
 20425  		if( rc == SXERR_ABORT ){
 20426  			SySetRelease(&sExprNode);
 20427  			return SXERR_ABORT;
 20428  		}
 20429  	}
 20430  	SySetRelease(&sExprNode);
 20431  	return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
 20432  }
 20433  /*
 20434   * Return a pointer to the node construct handler associated
 20435   * with a given node type [i.e: string, integer, float, ...].
 20436   */
 20437  JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
 20438  {
 20439  	if( nNodeType & JX9_TK_NUM ){
 20440  		/* Numeric literal: Either real or integer */
 20441  		return jx9CompileNumLiteral;
 20442  	}else if( nNodeType & JX9_TK_DSTR ){
 20443  		/* Double quoted string */
 20444  		return jx9CompileString;
 20445  	}else if( nNodeType & JX9_TK_SSTR ){
 20446  		/* Single quoted string */
 20447  		return jx9CompileSimpleString;
 20448  	}else if( nNodeType & JX9_TK_NOWDOC ){
 20449  		/* Nowdoc */
 20450  		return jx9CompileNowdoc;
 20451  	}
 20452  	return 0;
 20453  }
 20454  /*
 20455   * Jx9 Language construct table.
 20456   */
 20457  static const LangConstruct aLangConstruct[] = {
 20458  	{ JX9_TKWRD_IF,       jx9CompileIf     },
 20459  	{ JX9_TKWRD_FUNCTION, jx9CompileFunction  },
 20460  	{ JX9_TKWRD_FOREACH,  jx9CompileForeach },
 20461  	{ JX9_TKWRD_WHILE,    jx9CompileWhile  },
 20462  	{ JX9_TKWRD_FOR,      jx9CompileFor    },
 20463  	{ JX9_TKWRD_SWITCH,   jx9CompileSwitch },
 20464  	{ JX9_TKWRD_DIE,      jx9CompileHalt   },
 20465  	{ JX9_TKWRD_EXIT,     jx9CompileHalt   },
 20466  	{ JX9_TKWRD_RETURN,   jx9CompileReturn },
 20467  	{ JX9_TKWRD_BREAK,    jx9CompileBreak  },
 20468  	{ JX9_TKWRD_CONTINUE, jx9CompileContinue  },
 20469  	{ JX9_TKWRD_STATIC,   jx9CompileStatic    },
 20470  	{ JX9_TKWRD_UPLINK,   jx9CompileUplink  },
 20471  	{ JX9_TKWRD_CONST,    jx9CompileConstant  },
 20472  };
 20473  /*
 20474   * Return a pointer to the statement handler routine associated
 20475   * with a given JX9 keyword [i.e: if, for, while, ...].
 20476   */
 20477  static ProcLangConstruct GenStateGetStatementHandler(
 20478  	sxu32 nKeywordID   /* Keyword  ID*/
 20479  	)
 20480  {
 20481  	sxu32 n = 0;
 20482  	for(;;){
 20483  		if( n >= SX_ARRAYSIZE(aLangConstruct) ){
 20484  			break;
 20485  		}
 20486  		if( aLangConstruct[n].nID == nKeywordID ){
 20487  			/* Return a pointer to the handler.
 20488  			*/
 20489  			return aLangConstruct[n].xConstruct;
 20490  		}
 20491  		n++;
 20492  	}
 20493  	/* Not a language construct */
 20494  	return 0;
 20495  }
 20496  /*
 20497   * Compile a jx9 program.
 20498   * If something goes wrong while compiling the Jx9 chunk, this function
 20499   * takes care of generating the appropriate error message.
 20500   */
 20501  static sxi32 GenStateCompileChunk(
 20502  	jx9_gen_state *pGen, /* Code generator state */
 20503  	sxi32 iFlags             /* Compile flags */
 20504  	)
 20505  {
 20506  	ProcLangConstruct xCons;
 20507  	sxi32 rc;
 20508  	rc = SXRET_OK; /* Prevent compiler warning */
 20509  	for(;;){
 20510  		if( pGen->pIn >= pGen->pEnd ){
 20511  			/* No more input to process */
 20512  			break;
 20513  		}
 20514  		xCons = 0;
 20515  		if( pGen->pIn->nType & JX9_TK_KEYWORD ){
 20516  			sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
 20517  			/* Try to extract a language construct handler */
 20518  			xCons = GenStateGetStatementHandler(nKeyword);
 20519  			if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
 20520  				rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
 20521  					"Syntax error: Unexpected keyword '%z'",
 20522  					&pGen->pIn->sData);
 20523  				if( rc == SXERR_ABORT ){
 20524  					break;
 20525  				}
 20526  				/* Synchronize with the first semi-colon and avoid compiling
 20527  				 * this erroneous statement.
 20528  				 */
 20529  				xCons = jx9ErrorRecover;
 20530  			}
 20531  		}
 20532  		if( xCons == 0 ){
 20533  			/* Assume an expression an try to compile it */
 20534  			rc = jx9CompileExpr(&(*pGen), 0, 0);
 20535  			if(  rc != SXERR_EMPTY ){
 20536  				/* Pop l-value */
 20537  				jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
 20538  			}
 20539  		}else{
 20540  			/* Go compile the sucker */
 20541  			rc = xCons(&(*pGen));
 20542  		}
 20543  		if( rc == SXERR_ABORT ){
 20544  			/* Request to abort compilation */
 20545  			break;
 20546  		}
 20547  		/* Ignore trailing semi-colons ';' */
 20548  		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
 20549  			pGen->pIn++;
 20550  		}
 20551  		if( iFlags & JX9_COMPILE_SINGLE_STMT ){
 20552  			/* Compile a single statement and return */
 20553  			break;
 20554  		}
 20555  		/* LOOP ONE */
 20556  		/* LOOP TWO */
 20557  		/* LOOP THREE */
 20558  		/* LOOP FOUR */
 20559  	}
 20560  	/* Return compilation status */
 20561  	return rc;
 20562  }
 20563  /*
 20564   * Compile a raw chunk. The raw chunk can contain JX9 code embedded
 20565   * in HTML, XML and so on. This function handle all the stuff.
 20566   * This is the only compile interface exported from this file.
 20567   */
 20568  JX9_PRIVATE sxi32 jx9CompileScript(
 20569  	jx9_vm *pVm,        /* Generate JX9 bytecodes for this Virtual Machine */
 20570  	SyString *pScript,  /* Script to compile */
 20571  	sxi32 iFlags        /* Compile flags */
 20572  	)
 20573  {
 20574  	jx9_gen_state *pGen;
 20575  	SySet aToken;
 20576  	sxi32 rc;
 20577  	if( pScript->nByte < 1 ){
 20578  		/* Nothing to compile */
 20579  		return JX9_OK;
 20580  	}
 20581  	/* Initialize the tokens containers */
 20582  	SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
 20583  	SySetAlloc(&aToken, 0xc0);
 20584  	pGen = &pVm->sCodeGen;
 20585  	rc = JX9_OK;
 20586  	/* Tokenize the JX9 chunk first */
 20587  	jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
 20588  	if( SySetUsed(&aToken) < 1 ){
 20589  		return SXERR_EMPTY;
 20590  	}
 20591  	/* Point to the head and tail of the token stream. */
 20592  	pGen->pIn  = (SyToken *)SySetBasePtr(&aToken);
 20593  	pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
 20594  	/* Compile the chunk */
 20595  	rc = GenStateCompileChunk(pGen,iFlags);
 20596  	/* Cleanup */
 20597  	SySetRelease(&aToken);
 20598  	return rc;
 20599  }
 20600  /*
 20601   * Utility routines.Initialize the code generator.
 20602   */
 20603  JX9_PRIVATE sxi32 jx9InitCodeGenerator(
 20604  	jx9_vm *pVm,       /* Target VM */
 20605  	ProcConsumer xErr, /* Error log consumer callabck  */
 20606  	void *pErrData     /* Last argument to xErr() */
 20607  	)
 20608  {
 20609  	jx9_gen_state *pGen = &pVm->sCodeGen;
 20610  	/* Zero the structure */
 20611  	SyZero(pGen, sizeof(jx9_gen_state));
 20612  	/* Initial state */
 20613  	pGen->pVm  = &(*pVm);
 20614  	pGen->xErr = xErr;
 20615  	pGen->pErrData = pErrData;
 20616  	SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
 20617  	SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
 20618  	/* Create the global scope */
 20619  	GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
 20620  	/* Point to the global scope */
 20621  	pGen->pCurrent = &pGen->sGlobal;
 20622  	return SXRET_OK;
 20623  }
 20624  /*
 20625   * Utility routines. Reset the code generator to it's initial state.
 20626   */
 20627  JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
 20628  	jx9_vm *pVm,       /* Target VM */
 20629  	ProcConsumer xErr, /* Error log consumer callabck  */
 20630  	void *pErrData     /* Last argument to xErr() */
 20631  	)
 20632  {
 20633  	jx9_gen_state *pGen = &pVm->sCodeGen;
 20634  	GenBlock *pBlock, *pParent;
 20635  	/* Point to the global scope */
 20636  	pBlock = pGen->pCurrent;
 20637  	while( pBlock->pParent != 0 ){
 20638  		pParent = pBlock->pParent;
 20639  		GenStateFreeBlock(pBlock);
 20640  		pBlock = pParent;
 20641  	}
 20642  	pGen->xErr = xErr;
 20643  	pGen->pErrData = pErrData;
 20644  	pGen->pCurrent = &pGen->sGlobal;
 20645  	pGen->pIn = pGen->pEnd = 0;
 20646  	pGen->nErr = 0;
 20647  	return SXRET_OK;
 20648  }
 20649  /*
 20650   * Generate a compile-time error message.
 20651   * If the error count limit is reached (usually 15 error message)
 20652   * this function return SXERR_ABORT.In that case upper-layers must
 20653   * abort compilation immediately.
 20654   */
 20655  JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
 20656  {
 20657  	SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
 20658  	const char *zErr = "Error";
 20659  	va_list ap;
 20660  	if( nErrType == E_ERROR ){
 20661  		/* Increment the error counter */
 20662  		pGen->nErr++;
 20663  		if( pGen->nErr > 15 ){
 20664  			/* Error count limit reached */
 20665  			SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);	
 20666  			/* Abort immediately */
 20667  			return SXERR_ABORT;
 20668  		}
 20669  	}
 20670  	switch(nErrType){
 20671  	case E_WARNING: zErr = "Warning";     break;
 20672  	case E_PARSE:   zErr = "Parse error"; break;
 20673  	case E_NOTICE:  zErr = "Notice";      break;
 20674  	default:
 20675  		break;
 20676  	}
 20677  	/* Format the error message */
 20678  	SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
 20679  	va_start(ap, zFormat);
 20680  	SyBlobFormatAp(pWorker, zFormat, ap);
 20681  	va_end(ap);
 20682  	/* Append a new line */
 20683  	SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
 20684  	return JX9_OK;
 20685  }
 20686  /*
 20687   * ----------------------------------------------------------
 20688   * File: jx9_const.c
 20689   * MD5: f3980b00dd1eda0bb2b749424a8dfffe
 20690   * ----------------------------------------------------------
 20691   */
 20692  /*
 20693   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 20694   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 20695   * Version 1.7.2
 20696   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 20697   * please contact Symisc Systems via:
 20698   *       legal@symisc.net
 20699   *       licensing@symisc.net
 20700   *       contact@symisc.net
 20701   * or visit:
 20702   *      http://jx9.symisc.net/
 20703   */
 20704   /* $SymiscID: const.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
 20705  #ifndef JX9_AMALGAMATION
 20706  #include "jx9Int.h"
 20707  #endif
 20708  /* This file implement built-in constants for the JX9 engine. */
 20709  /*
 20710   * JX9_VERSION
 20711   * __JX9__
 20712   *   Expand the current version of the JX9 engine.
 20713   */
 20714  static void JX9_VER_Const(jx9_value *pVal, void *pUnused)
 20715  {
 20716  	SXUNUSED(pUnused);
 20717  	jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/);
 20718  }
 20719  #ifdef __WINNT__
 20720  #include <Windows.h>
 20721  #elif defined(__UNIXES__)
 20722  #include <sys/utsname.h>
 20723  #endif
 20724  /*
 20725   * JX9_OS
 20726   * __OS__
 20727   *  Expand the name of the host Operating System.
 20728   */
 20729  static void JX9_OS_Const(jx9_value *pVal, void *pUnused)
 20730  {
 20731  #if defined(__WINNT__)
 20732  	jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1);
 20733  #elif defined(__UNIXES__)
 20734  	struct utsname sInfo;
 20735  	if( uname(&sInfo) != 0 ){
 20736  		jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1);
 20737  	}else{
 20738  		jx9_value_string(pVal, sInfo.sysname, -1);
 20739  	}
 20740  #else
 20741  	jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1);
 20742  #endif
 20743  	SXUNUSED(pUnused);
 20744  }
 20745  /*
 20746   * JX9_EOL
 20747   *  Expand the correct 'End Of Line' symbol for this platform.
 20748   */
 20749  static void JX9_EOL_Const(jx9_value *pVal, void *pUnused)
 20750  {
 20751  	SXUNUSED(pUnused);
 20752  #ifdef __WINNT__
 20753  	jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1);
 20754  #else
 20755  	jx9_value_string(pVal, "\n", (int)sizeof(char));
 20756  #endif
 20757  }
 20758  /*
 20759   * JX9_INT_MAX
 20760   * Expand the largest integer supported.
 20761   * Note that JX9 deals with 64-bit integer for all platforms.
 20762   */
 20763  static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused)
 20764  {
 20765  	SXUNUSED(pUnused);
 20766  	jx9_value_int64(pVal, SXI64_HIGH);
 20767  }
 20768  /*
 20769   * JX9_INT_SIZE
 20770   * Expand the size in bytes of a 64-bit integer.
 20771   */
 20772  static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused)
 20773  {
 20774  	SXUNUSED(pUnused);
 20775  	jx9_value_int64(pVal, sizeof(sxi64));
 20776  }
 20777  /*
 20778   * DIRECTORY_SEPARATOR.
 20779   * Expand the directory separator character.
 20780   */
 20781  static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused)
 20782  {
 20783  	SXUNUSED(pUnused);
 20784  #ifdef __WINNT__
 20785  	jx9_value_string(pVal, "\\", (int)sizeof(char));
 20786  #else
 20787  	jx9_value_string(pVal, "/", (int)sizeof(char));
 20788  #endif
 20789  }
 20790  /*
 20791   * PATH_SEPARATOR.
 20792   * Expand the path separator character.
 20793   */
 20794  static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused)
 20795  {
 20796  	SXUNUSED(pUnused);
 20797  #ifdef __WINNT__
 20798  	jx9_value_string(pVal, ";", (int)sizeof(char));
 20799  #else
 20800  	jx9_value_string(pVal, ":", (int)sizeof(char));
 20801  #endif
 20802  }
 20803  #ifndef __WINNT__
 20804  #include <time.h>
 20805  #endif
 20806  /*
 20807   * __TIME__
 20808   *  Expand the current time (GMT).
 20809   */
 20810  static void JX9_TIME_Const(jx9_value *pVal, void *pUnused)
 20811  {
 20812  	Sytm sTm;
 20813  #ifdef __WINNT__
 20814  	SYSTEMTIME sOS;
 20815  	GetSystemTime(&sOS);
 20816  	SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 20817  #else
 20818  	struct tm *pTm;
 20819  	time_t t;
 20820  	time(&t);
 20821  	pTm = gmtime(&t);
 20822  	STRUCT_TM_TO_SYTM(pTm, &sTm);
 20823  #endif
 20824  	SXUNUSED(pUnused); /* cc warning */
 20825  	/* Expand */
 20826  	jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec);
 20827  }
 20828  /*
 20829   * __DATE__
 20830   *  Expand the current date in the ISO-8601 format.
 20831   */
 20832  static void JX9_DATE_Const(jx9_value *pVal, void *pUnused)
 20833  {
 20834  	Sytm sTm;
 20835  #ifdef __WINNT__
 20836  	SYSTEMTIME sOS;
 20837  	GetSystemTime(&sOS);
 20838  	SYSTEMTIME_TO_SYTM(&sOS, &sTm);
 20839  #else
 20840  	struct tm *pTm;
 20841  	time_t t;
 20842  	time(&t);
 20843  	pTm = gmtime(&t);
 20844  	STRUCT_TM_TO_SYTM(pTm, &sTm);
 20845  #endif
 20846  	SXUNUSED(pUnused); /* cc warning */
 20847  	/* Expand */
 20848  	jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday);
 20849  }
 20850  /*
 20851   * __FILE__
 20852   *  Path of the processed script.
 20853   */
 20854  static void JX9_FILE_Const(jx9_value *pVal, void *pUserData)
 20855  {
 20856  	jx9_vm *pVm = (jx9_vm *)pUserData;
 20857  	SyString *pFile;
 20858  	/* Peek the top entry */
 20859  	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 20860  	if( pFile == 0 ){
 20861  		/* Expand the magic word: ":MEMORY:" */
 20862  		jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
 20863  	}else{
 20864  		jx9_value_string(pVal, pFile->zString, pFile->nByte);
 20865  	}
 20866  }
 20867  /*
 20868   * __DIR__
 20869   *  Directory holding the processed script.
 20870   */
 20871  static void JX9_DIR_Const(jx9_value *pVal, void *pUserData)
 20872  {
 20873  	jx9_vm *pVm = (jx9_vm *)pUserData;
 20874  	SyString *pFile;
 20875  	/* Peek the top entry */
 20876  	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 20877  	if( pFile == 0 ){
 20878  		/* Expand the magic word: ":MEMORY:" */
 20879  		jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
 20880  	}else{
 20881  		if( pFile->nByte > 0 ){
 20882  			const char *zDir;
 20883  			int nLen;
 20884  			zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen);
 20885  			jx9_value_string(pVal, zDir, nLen);
 20886  		}else{
 20887  			/* Expand '.' as the current directory*/
 20888  			jx9_value_string(pVal, ".", (int)sizeof(char));
 20889  		}
 20890  	}
 20891  }
 20892  /*
 20893   * E_ERROR
 20894   *  Expands 1
 20895   */
 20896  static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData)
 20897  {
 20898  	jx9_value_int(pVal, 1);
 20899  	SXUNUSED(pUserData);
 20900  }
 20901  /*
 20902   * E_WARNING
 20903   *  Expands 2
 20904   */
 20905  static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData)
 20906  {
 20907  	jx9_value_int(pVal, 2);
 20908  	SXUNUSED(pUserData);
 20909  }
 20910  /*
 20911   * E_PARSE
 20912   *  Expands 4
 20913   */
 20914  static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData)
 20915  {
 20916  	jx9_value_int(pVal, 4);
 20917  	SXUNUSED(pUserData);
 20918  }
 20919  /*
 20920   * E_NOTICE
 20921   * Expands 8
 20922   */
 20923  static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData)
 20924  {
 20925  	jx9_value_int(pVal, 8);
 20926  	SXUNUSED(pUserData);
 20927  }
 20928  /*
 20929   * CASE_LOWER
 20930   *  Expands 0.
 20931   */
 20932  static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData)
 20933  {
 20934  	jx9_value_int(pVal, 0);
 20935  	SXUNUSED(pUserData);
 20936  }
 20937  /*
 20938   * CASE_UPPER
 20939   *  Expands 1.
 20940   */
 20941  static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData)
 20942  {
 20943  	jx9_value_int(pVal, 1);
 20944  	SXUNUSED(pUserData);
 20945  }
 20946  /*
 20947   * STR_PAD_LEFT
 20948   *  Expands 0.
 20949   */
 20950  static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData)
 20951  {
 20952  	jx9_value_int(pVal, 0);
 20953  	SXUNUSED(pUserData);
 20954  }
 20955  /*
 20956   * STR_PAD_RIGHT
 20957   *  Expands 1.
 20958   */
 20959  static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData)
 20960  {
 20961  	jx9_value_int(pVal, 1);
 20962  	SXUNUSED(pUserData);
 20963  }
 20964  /*
 20965   * STR_PAD_BOTH
 20966   *  Expands 2.
 20967   */
 20968  static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData)
 20969  {
 20970  	jx9_value_int(pVal, 2);
 20971  	SXUNUSED(pUserData);
 20972  }
 20973  /*
 20974   * COUNT_NORMAL
 20975   *  Expands 0
 20976   */
 20977  static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData)
 20978  {
 20979  	jx9_value_int(pVal, 0);
 20980  	SXUNUSED(pUserData);
 20981  }
 20982  /*
 20983   * COUNT_RECURSIVE
 20984   *  Expands 1.
 20985   */
 20986  static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData)
 20987  {
 20988  	jx9_value_int(pVal, 1);
 20989  	SXUNUSED(pUserData);
 20990  }
 20991  /*
 20992   * SORT_ASC
 20993   *  Expands 1.
 20994   */
 20995  static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData)
 20996  {
 20997  	jx9_value_int(pVal, 1);
 20998  	SXUNUSED(pUserData);
 20999  }
 21000  /*
 21001   * SORT_DESC
 21002   *  Expands 2.
 21003   */
 21004  static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData)
 21005  {
 21006  	jx9_value_int(pVal, 2);
 21007  	SXUNUSED(pUserData);
 21008  }
 21009  /*
 21010   * SORT_REGULAR
 21011   *  Expands 3.
 21012   */
 21013  static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData)
 21014  {
 21015  	jx9_value_int(pVal, 3);
 21016  	SXUNUSED(pUserData);
 21017  }
 21018  /*
 21019   * SORT_NUMERIC
 21020   *  Expands 4.
 21021   */
 21022  static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData)
 21023  {
 21024  	jx9_value_int(pVal, 4);
 21025  	SXUNUSED(pUserData);
 21026  }
 21027  /*
 21028   * SORT_STRING
 21029   *  Expands 5.
 21030   */
 21031  static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData)
 21032  {
 21033  	jx9_value_int(pVal, 5);
 21034  	SXUNUSED(pUserData);
 21035  }
 21036  /*
 21037   * JX9_ROUND_HALF_UP
 21038   *  Expands 1.
 21039   */
 21040  static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData)
 21041  {
 21042  	jx9_value_int(pVal, 1);
 21043  	SXUNUSED(pUserData);
 21044  }
 21045  /*
 21046   * SJX9_ROUND_HALF_DOWN
 21047   *  Expands 2.
 21048   */
 21049  static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData)
 21050  {
 21051  	jx9_value_int(pVal, 2);
 21052  	SXUNUSED(pUserData);
 21053  }
 21054  /*
 21055   * JX9_ROUND_HALF_EVEN
 21056   *  Expands 3.
 21057   */
 21058  static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData)
 21059  {
 21060  	jx9_value_int(pVal, 3);
 21061  	SXUNUSED(pUserData);
 21062  }
 21063  /*
 21064   * JX9_ROUND_HALF_ODD
 21065   *  Expands 4.
 21066   */
 21067  static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData)
 21068  {
 21069  	jx9_value_int(pVal, 4);
 21070  	SXUNUSED(pUserData);
 21071  }
 21072  #ifdef JX9_ENABLE_MATH_FUNC
 21073  /*
 21074   * PI
 21075   *  Expand the value of pi.
 21076   */
 21077  static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData)
 21078  {
 21079  	SXUNUSED(pUserData); /* cc warning */
 21080  	jx9_value_double(pVal, JX9_PI);
 21081  }
 21082  /*
 21083   * M_E
 21084   *  Expand 2.7182818284590452354
 21085   */
 21086  static void JX9_M_E_Const(jx9_value *pVal, void *pUserData)
 21087  {
 21088  	SXUNUSED(pUserData); /* cc warning */
 21089  	jx9_value_double(pVal, 2.7182818284590452354);
 21090  }
 21091  /*
 21092   * M_LOG2E
 21093   *  Expand 2.7182818284590452354
 21094   */
 21095  static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData)
 21096  {
 21097  	SXUNUSED(pUserData); /* cc warning */
 21098  	jx9_value_double(pVal, 1.4426950408889634074);
 21099  }
 21100  /*
 21101   * M_LOG10E
 21102   *  Expand 0.4342944819032518276
 21103   */
 21104  static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData)
 21105  {
 21106  	SXUNUSED(pUserData); /* cc warning */
 21107  	jx9_value_double(pVal, 0.4342944819032518276);
 21108  }
 21109  /*
 21110   * M_LN2
 21111   *  Expand 	0.69314718055994530942
 21112   */
 21113  static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData)
 21114  {
 21115  	SXUNUSED(pUserData); /* cc warning */
 21116  	jx9_value_double(pVal, 0.69314718055994530942);
 21117  }
 21118  /*
 21119   * M_LN10
 21120   *  Expand 	2.30258509299404568402
 21121   */
 21122  static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData)
 21123  {
 21124  	SXUNUSED(pUserData); /* cc warning */
 21125  	jx9_value_double(pVal, 2.30258509299404568402);
 21126  }
 21127  /*
 21128   * M_PI_2
 21129   *  Expand 	1.57079632679489661923
 21130   */
 21131  static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData)
 21132  {
 21133  	SXUNUSED(pUserData); /* cc warning */
 21134  	jx9_value_double(pVal, 1.57079632679489661923);
 21135  }
 21136  /*
 21137   * M_PI_4
 21138   *  Expand 	0.78539816339744830962
 21139   */
 21140  static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData)
 21141  {
 21142  	SXUNUSED(pUserData); /* cc warning */
 21143  	jx9_value_double(pVal, 0.78539816339744830962);
 21144  }
 21145  /*
 21146   * M_1_PI
 21147   *  Expand 	0.31830988618379067154
 21148   */
 21149  static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData)
 21150  {
 21151  	SXUNUSED(pUserData); /* cc warning */
 21152  	jx9_value_double(pVal, 0.31830988618379067154);
 21153  }
 21154  /*
 21155   * M_2_PI
 21156   *  Expand 0.63661977236758134308
 21157   */
 21158  static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData)
 21159  {
 21160  	SXUNUSED(pUserData); /* cc warning */
 21161  	jx9_value_double(pVal, 0.63661977236758134308);
 21162  }
 21163  /*
 21164   * M_SQRTPI
 21165   *  Expand 1.77245385090551602729
 21166   */
 21167  static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData)
 21168  {
 21169  	SXUNUSED(pUserData); /* cc warning */
 21170  	jx9_value_double(pVal, 1.77245385090551602729);
 21171  }
 21172  /*
 21173   * M_2_SQRTPI
 21174   *  Expand 	1.12837916709551257390
 21175   */
 21176  static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData)
 21177  {
 21178  	SXUNUSED(pUserData); /* cc warning */
 21179  	jx9_value_double(pVal, 1.12837916709551257390);
 21180  }
 21181  /*
 21182   * M_SQRT2
 21183   *  Expand 	1.41421356237309504880
 21184   */
 21185  static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData)
 21186  {
 21187  	SXUNUSED(pUserData); /* cc warning */
 21188  	jx9_value_double(pVal, 1.41421356237309504880);
 21189  }
 21190  /*
 21191   * M_SQRT3
 21192   *  Expand 	1.73205080756887729352
 21193   */
 21194  static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData)
 21195  {
 21196  	SXUNUSED(pUserData); /* cc warning */
 21197  	jx9_value_double(pVal, 1.73205080756887729352);
 21198  }
 21199  /*
 21200   * M_SQRT1_2
 21201   *  Expand 	0.70710678118654752440
 21202   */
 21203  static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData)
 21204  {
 21205  	SXUNUSED(pUserData); /* cc warning */
 21206  	jx9_value_double(pVal, 0.70710678118654752440);
 21207  }
 21208  /*
 21209   * M_LNPI
 21210   *  Expand 	1.14472988584940017414
 21211   */
 21212  static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData)
 21213  {
 21214  	SXUNUSED(pUserData); /* cc warning */
 21215  	jx9_value_double(pVal, 1.14472988584940017414);
 21216  }
 21217  /*
 21218   * M_EULER
 21219   *  Expand  0.57721566490153286061
 21220   */
 21221  static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData)
 21222  {
 21223  	SXUNUSED(pUserData); /* cc warning */
 21224  	jx9_value_double(pVal, 0.57721566490153286061);
 21225  }
 21226  #endif /* JX9_DISABLE_BUILTIN_MATH */
 21227  /*
 21228   * DATE_ATOM
 21229   *  Expand Atom (example: 2005-08-15T15:52:01+00:00) 
 21230   */
 21231  static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData)
 21232  {
 21233  	SXUNUSED(pUserData); /* cc warning */
 21234  	jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
 21235  }
 21236  /*
 21237   * DATE_COOKIE
 21238   *  HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC)  
 21239   */
 21240  static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData)
 21241  {
 21242  	SXUNUSED(pUserData); /* cc warning */
 21243  	jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
 21244  }
 21245  /*
 21246   * DATE_ISO8601
 21247   *  ISO-8601 (example: 2005-08-15T15:52:01+0000) 
 21248   */
 21249  static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData)
 21250  {
 21251  	SXUNUSED(pUserData); /* cc warning */
 21252  	jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/);
 21253  }
 21254  /*
 21255   * DATE_RFC822
 21256   *  RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000) 
 21257   */
 21258  static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData)
 21259  {
 21260  	SXUNUSED(pUserData); /* cc warning */
 21261  	jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
 21262  }
 21263  /*
 21264   * DATE_RFC850
 21265   *  RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC) 
 21266   */
 21267  static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData)
 21268  {
 21269  	SXUNUSED(pUserData); /* cc warning */
 21270  	jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
 21271  }
 21272  /*
 21273   * DATE_RFC1036
 21274   *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) 
 21275   */
 21276  static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData)
 21277  {
 21278  	SXUNUSED(pUserData); /* cc warning */
 21279  	jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
 21280  }
 21281  /*
 21282   * DATE_RFC1123
 21283   *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)  
 21284   */
 21285  static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData)
 21286  {
 21287  	SXUNUSED(pUserData); /* cc warning */
 21288  	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
 21289  }
 21290  /*
 21291   * DATE_RFC2822
 21292   *  RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000)  
 21293   */
 21294  static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData)
 21295  {
 21296  	SXUNUSED(pUserData); /* cc warning */
 21297  	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
 21298  }
 21299  /*
 21300   * DATE_RSS
 21301   *  RSS (Mon, 15 Aug 2005 15:52:01 +0000) 
 21302   */
 21303  static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData)
 21304  {
 21305  	SXUNUSED(pUserData); /* cc warning */
 21306  	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
 21307  }
 21308  /*
 21309   * DATE_W3C
 21310   *  World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00) 
 21311   */
 21312  static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData)
 21313  {
 21314  	SXUNUSED(pUserData); /* cc warning */
 21315  	jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
 21316  }
 21317  /*
 21318   * ENT_COMPAT
 21319   *  Expand 0x01 (Must be a power of two)
 21320   */
 21321  static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData)
 21322  {
 21323  	SXUNUSED(pUserData); /* cc warning */
 21324  	jx9_value_int(pVal, 0x01);
 21325  }
 21326  /*
 21327   * ENT_QUOTES
 21328   *  Expand 0x02 (Must be a power of two)
 21329   */
 21330  static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData)
 21331  {
 21332  	SXUNUSED(pUserData); /* cc warning */
 21333  	jx9_value_int(pVal, 0x02);
 21334  }
 21335  /*
 21336   * ENT_NOQUOTES
 21337   *  Expand 0x04 (Must be a power of two)
 21338   */
 21339  static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData)
 21340  {
 21341  	SXUNUSED(pUserData); /* cc warning */
 21342  	jx9_value_int(pVal, 0x04);
 21343  }
 21344  /*
 21345   * ENT_IGNORE
 21346   *  Expand 0x08 (Must be a power of two)
 21347   */
 21348  static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData)
 21349  {
 21350  	SXUNUSED(pUserData); /* cc warning */
 21351  	jx9_value_int(pVal, 0x08);
 21352  }
 21353  /*
 21354   * ENT_SUBSTITUTE
 21355   *  Expand 0x10 (Must be a power of two)
 21356   */
 21357  static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData)
 21358  {
 21359  	SXUNUSED(pUserData); /* cc warning */
 21360  	jx9_value_int(pVal, 0x10);
 21361  }
 21362  /*
 21363   * ENT_DISALLOWED
 21364   *  Expand 0x20 (Must be a power of two)
 21365   */
 21366  static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData)
 21367  {
 21368  	SXUNUSED(pUserData); /* cc warning */
 21369  	jx9_value_int(pVal, 0x20);
 21370  }
 21371  /*
 21372   * ENT_HTML401
 21373   *  Expand 0x40 (Must be a power of two)
 21374   */
 21375  static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData)
 21376  {
 21377  	SXUNUSED(pUserData); /* cc warning */
 21378  	jx9_value_int(pVal, 0x40);
 21379  }
 21380  /*
 21381   * ENT_XML1
 21382   *  Expand 0x80 (Must be a power of two)
 21383   */
 21384  static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData)
 21385  {
 21386  	SXUNUSED(pUserData); /* cc warning */
 21387  	jx9_value_int(pVal, 0x80);
 21388  }
 21389  /*
 21390   * ENT_XHTML
 21391   *  Expand 0x100 (Must be a power of two)
 21392   */
 21393  static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData)
 21394  {
 21395  	SXUNUSED(pUserData); /* cc warning */
 21396  	jx9_value_int(pVal, 0x100);
 21397  }
 21398  /*
 21399   * ENT_HTML5
 21400   *  Expand 0x200 (Must be a power of two)
 21401   */
 21402  static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData)
 21403  {
 21404  	SXUNUSED(pUserData); /* cc warning */
 21405  	jx9_value_int(pVal, 0x200);
 21406  }
 21407  /*
 21408   * ISO-8859-1
 21409   * ISO_8859_1
 21410   *   Expand 1
 21411   */
 21412  static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData)
 21413  {
 21414  	SXUNUSED(pUserData); /* cc warning */
 21415  	jx9_value_int(pVal, 1);
 21416  }
 21417  /*
 21418   * UTF-8
 21419   * UTF8
 21420   *  Expand 2
 21421   */
 21422  static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData)
 21423  {
 21424  	SXUNUSED(pUserData); /* cc warning */
 21425  	jx9_value_int(pVal, 1);
 21426  }
 21427  /*
 21428   * HTML_ENTITIES
 21429   *  Expand 1
 21430   */
 21431  static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData)
 21432  {
 21433  	SXUNUSED(pUserData); /* cc warning */
 21434  	jx9_value_int(pVal, 1);
 21435  }
 21436  /*
 21437   * HTML_SPECIALCHARS
 21438   *  Expand 2
 21439   */
 21440  static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData)
 21441  {
 21442  	SXUNUSED(pUserData); /* cc warning */
 21443  	jx9_value_int(pVal, 2);
 21444  }
 21445  /*
 21446   * JX9_URL_SCHEME.
 21447   * Expand 1
 21448   */
 21449  static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData)
 21450  {
 21451  	SXUNUSED(pUserData); /* cc warning */
 21452  	jx9_value_int(pVal, 1);
 21453  }
 21454  /*
 21455   * JX9_URL_HOST.
 21456   * Expand 2
 21457   */
 21458  static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData)
 21459  {
 21460  	SXUNUSED(pUserData); /* cc warning */
 21461  	jx9_value_int(pVal, 2);
 21462  }
 21463  /*
 21464   * JX9_URL_PORT.
 21465   * Expand 3
 21466   */
 21467  static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData)
 21468  {
 21469  	SXUNUSED(pUserData); /* cc warning */
 21470  	jx9_value_int(pVal, 3);
 21471  }
 21472  /*
 21473   * JX9_URL_USER.
 21474   * Expand 4
 21475   */
 21476  static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData)
 21477  {
 21478  	SXUNUSED(pUserData); /* cc warning */
 21479  	jx9_value_int(pVal, 4);
 21480  }
 21481  /*
 21482   * JX9_URL_PASS.
 21483   * Expand 5
 21484   */
 21485  static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData)
 21486  {
 21487  	SXUNUSED(pUserData); /* cc warning */
 21488  	jx9_value_int(pVal, 5);
 21489  }
 21490  /*
 21491   * JX9_URL_PATH.
 21492   * Expand 6
 21493   */
 21494  static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData)
 21495  {
 21496  	SXUNUSED(pUserData); /* cc warning */
 21497  	jx9_value_int(pVal, 6);
 21498  }
 21499  /*
 21500   * JX9_URL_QUERY.
 21501   * Expand 7
 21502   */
 21503  static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData)
 21504  {
 21505  	SXUNUSED(pUserData); /* cc warning */
 21506  	jx9_value_int(pVal, 7);
 21507  }
 21508  /*
 21509   * JX9_URL_FRAGMENT.
 21510   * Expand 8
 21511   */
 21512  static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData)
 21513  {
 21514  	SXUNUSED(pUserData); /* cc warning */
 21515  	jx9_value_int(pVal, 8);
 21516  }
 21517  /*
 21518   * JX9_QUERY_RFC1738
 21519   * Expand 1
 21520   */
 21521  static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData)
 21522  {
 21523  	SXUNUSED(pUserData); /* cc warning */
 21524  	jx9_value_int(pVal, 1);
 21525  }
 21526  /*
 21527   * JX9_QUERY_RFC3986
 21528   * Expand 1
 21529   */
 21530  static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData)
 21531  {
 21532  	SXUNUSED(pUserData); /* cc warning */
 21533  	jx9_value_int(pVal, 2);
 21534  }
 21535  /*
 21536   * FNM_NOESCAPE
 21537   *  Expand 0x01 (Must be a power of two)
 21538   */
 21539  static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
 21540  {
 21541  	SXUNUSED(pUserData); /* cc warning */
 21542  	jx9_value_int(pVal, 0x01);
 21543  }
 21544  /*
 21545   * FNM_PATHNAME
 21546   *  Expand 0x02 (Must be a power of two)
 21547   */
 21548  static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData)
 21549  {
 21550  	SXUNUSED(pUserData); /* cc warning */
 21551  	jx9_value_int(pVal, 0x02);
 21552  }
 21553  /*
 21554   * FNM_PERIOD
 21555   *  Expand 0x04 (Must be a power of two)
 21556   */
 21557  static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData)
 21558  {
 21559  	SXUNUSED(pUserData); /* cc warning */
 21560  	jx9_value_int(pVal, 0x04);
 21561  }
 21562  /*
 21563   * FNM_CASEFOLD
 21564   *  Expand 0x08 (Must be a power of two)
 21565   */
 21566  static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData)
 21567  {
 21568  	SXUNUSED(pUserData); /* cc warning */
 21569  	jx9_value_int(pVal, 0x08);
 21570  }
 21571  /*
 21572   * PATHINFO_DIRNAME
 21573   *  Expand 1.
 21574   */
 21575  static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData)
 21576  {
 21577  	SXUNUSED(pUserData); /* cc warning */
 21578  	jx9_value_int(pVal, 1);
 21579  }
 21580  /*
 21581   * PATHINFO_BASENAME
 21582   *  Expand 2.
 21583   */
 21584  static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData)
 21585  {
 21586  	SXUNUSED(pUserData); /* cc warning */
 21587  	jx9_value_int(pVal, 2);
 21588  }
 21589  /*
 21590   * PATHINFO_EXTENSION
 21591   *  Expand 3.
 21592   */
 21593  static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData)
 21594  {
 21595  	SXUNUSED(pUserData); /* cc warning */
 21596  	jx9_value_int(pVal, 3);
 21597  }
 21598  /*
 21599   * PATHINFO_FILENAME
 21600   *  Expand 4.
 21601   */
 21602  static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData)
 21603  {
 21604  	SXUNUSED(pUserData); /* cc warning */
 21605  	jx9_value_int(pVal, 4);
 21606  }
 21607  /*
 21608   * ASSERT_ACTIVE.
 21609   *  Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h
 21610   */
 21611  static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData)
 21612  {
 21613  	SXUNUSED(pUserData); /* cc warning */
 21614  	jx9_value_int(pVal, JX9_ASSERT_DISABLE);
 21615  }
 21616  /*
 21617   * ASSERT_WARNING.
 21618   *  Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h
 21619   */
 21620  static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData)
 21621  {
 21622  	SXUNUSED(pUserData); /* cc warning */
 21623  	jx9_value_int(pVal, JX9_ASSERT_WARNING);
 21624  }
 21625  /*
 21626   * ASSERT_BAIL.
 21627   *  Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h
 21628   */
 21629  static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData)
 21630  {
 21631  	SXUNUSED(pUserData); /* cc warning */
 21632  	jx9_value_int(pVal, JX9_ASSERT_BAIL);
 21633  }
 21634  /*
 21635   * ASSERT_QUIET_EVAL.
 21636   *  Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h
 21637   */
 21638  static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData)
 21639  {
 21640  	SXUNUSED(pUserData); /* cc warning */
 21641  	jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL);
 21642  }
 21643  /*
 21644   * ASSERT_CALLBACK.
 21645   *  Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h
 21646   */
 21647  static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData)
 21648  {
 21649  	SXUNUSED(pUserData); /* cc warning */
 21650  	jx9_value_int(pVal, JX9_ASSERT_CALLBACK);
 21651  }
 21652  /*
 21653   * SEEK_SET.
 21654   *  Expand 0
 21655   */
 21656  static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData)
 21657  {
 21658  	SXUNUSED(pUserData); /* cc warning */
 21659  	jx9_value_int(pVal, 0);
 21660  }
 21661  /*
 21662   * SEEK_CUR.
 21663   *  Expand 1
 21664   */
 21665  static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData)
 21666  {
 21667  	SXUNUSED(pUserData); /* cc warning */
 21668  	jx9_value_int(pVal, 1);
 21669  }
 21670  /*
 21671   * SEEK_END.
 21672   *  Expand 2
 21673   */
 21674  static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData)
 21675  {
 21676  	SXUNUSED(pUserData); /* cc warning */
 21677  	jx9_value_int(pVal, 2);
 21678  }
 21679  /*
 21680   * LOCK_SH.
 21681   *  Expand 2
 21682   */
 21683  static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData)
 21684  {
 21685  	SXUNUSED(pUserData); /* cc warning */
 21686  	jx9_value_int(pVal, 1);
 21687  }
 21688  /*
 21689   * LOCK_NB.
 21690   *  Expand 5
 21691   */
 21692  static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData)
 21693  {
 21694  	SXUNUSED(pUserData); /* cc warning */
 21695  	jx9_value_int(pVal, 5);
 21696  }
 21697  /*
 21698   * LOCK_EX.
 21699   *  Expand 0x01 (MUST BE A POWER OF TWO)
 21700   */
 21701  static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData)
 21702  {
 21703  	SXUNUSED(pUserData); /* cc warning */
 21704  	jx9_value_int(pVal, 0x01);
 21705  }
 21706  /*
 21707   * LOCK_UN.
 21708   *  Expand 0
 21709   */
 21710  static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData)
 21711  {
 21712  	SXUNUSED(pUserData); /* cc warning */
 21713  	jx9_value_int(pVal, 0);
 21714  }
 21715  /*
 21716   * FILE_USE_INC_PATH
 21717   *  Expand 0x01 (Must be a power of two)
 21718   */
 21719  static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData)
 21720  {
 21721  	SXUNUSED(pUserData); /* cc warning */
 21722  	jx9_value_int(pVal, 0x1);
 21723  }
 21724  /*
 21725   * FILE_IGN_NL
 21726   *  Expand 0x02 (Must be a power of two)
 21727   */
 21728  static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData)
 21729  {
 21730  	SXUNUSED(pUserData); /* cc warning */
 21731  	jx9_value_int(pVal, 0x2);
 21732  }
 21733  /*
 21734   * FILE_SKIP_EL
 21735   *  Expand 0x04 (Must be a power of two)
 21736   */
 21737  static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData)
 21738  {
 21739  	SXUNUSED(pUserData); /* cc warning */
 21740  	jx9_value_int(pVal, 0x4);
 21741  }
 21742  /*
 21743   * FILE_APPEND
 21744   *  Expand 0x08 (Must be a power of two)
 21745   */
 21746  static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData)
 21747  {
 21748  	SXUNUSED(pUserData); /* cc warning */
 21749  	jx9_value_int(pVal, 0x08);
 21750  }
 21751  /*
 21752   * SCANDIR_SORT_ASCENDING
 21753   *  Expand 0
 21754   */
 21755  static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData)
 21756  {
 21757  	SXUNUSED(pUserData); /* cc warning */
 21758  	jx9_value_int(pVal, 0);
 21759  }
 21760  /*
 21761   * SCANDIR_SORT_DESCENDING
 21762   *  Expand 1
 21763   */
 21764  static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData)
 21765  {
 21766  	SXUNUSED(pUserData); /* cc warning */
 21767  	jx9_value_int(pVal, 1);
 21768  }
 21769  /*
 21770   * SCANDIR_SORT_NONE
 21771   *  Expand 2
 21772   */
 21773  static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData)
 21774  {
 21775  	SXUNUSED(pUserData); /* cc warning */
 21776  	jx9_value_int(pVal, 2);
 21777  }
 21778  /*
 21779   * GLOB_MARK
 21780   *  Expand 0x01 (must be a power of two)
 21781   */
 21782  static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData)
 21783  {
 21784  	SXUNUSED(pUserData); /* cc warning */
 21785  	jx9_value_int(pVal, 0x01);
 21786  }
 21787  /*
 21788   * GLOB_NOSORT
 21789   *  Expand 0x02 (must be a power of two)
 21790   */
 21791  static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData)
 21792  {
 21793  	SXUNUSED(pUserData); /* cc warning */
 21794  	jx9_value_int(pVal, 0x02);
 21795  }
 21796  /*
 21797   * GLOB_NOCHECK
 21798   *  Expand 0x04 (must be a power of two)
 21799   */
 21800  static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData)
 21801  {
 21802  	SXUNUSED(pUserData); /* cc warning */
 21803  	jx9_value_int(pVal, 0x04);
 21804  }
 21805  /*
 21806   * GLOB_NOESCAPE
 21807   *  Expand 0x08 (must be a power of two)
 21808   */
 21809  static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
 21810  {
 21811  	SXUNUSED(pUserData); /* cc warning */
 21812  	jx9_value_int(pVal, 0x08);
 21813  }
 21814  /*
 21815   * GLOB_BRACE
 21816   *  Expand 0x10 (must be a power of two)
 21817   */
 21818  static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData)
 21819  {
 21820  	SXUNUSED(pUserData); /* cc warning */
 21821  	jx9_value_int(pVal, 0x10);
 21822  }
 21823  /*
 21824   * GLOB_ONLYDIR
 21825   *  Expand 0x20 (must be a power of two)
 21826   */
 21827  static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData)
 21828  {
 21829  	SXUNUSED(pUserData); /* cc warning */
 21830  	jx9_value_int(pVal, 0x20);
 21831  }
 21832  /*
 21833   * GLOB_ERR
 21834   *  Expand 0x40 (must be a power of two)
 21835   */
 21836  static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData)
 21837  {
 21838  	SXUNUSED(pUserData); /* cc warning */
 21839  	jx9_value_int(pVal, 0x40);
 21840  }
 21841  /*
 21842   * STDIN
 21843   *  Expand the STDIN handle as a resource.
 21844   */
 21845  static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData)
 21846  {
 21847  	jx9_vm *pVm = (jx9_vm *)pUserData;
 21848  	void *pResource;
 21849  	pResource = jx9ExportStdin(pVm);
 21850  	jx9_value_resource(pVal, pResource);
 21851  }
 21852  /*
 21853   * STDOUT
 21854   *   Expand the STDOUT handle as a resource.
 21855   */
 21856  static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData)
 21857  {
 21858  	jx9_vm *pVm = (jx9_vm *)pUserData;
 21859  	void *pResource;
 21860  	pResource = jx9ExportStdout(pVm);
 21861  	jx9_value_resource(pVal, pResource);
 21862  }
 21863  /*
 21864   * STDERR
 21865   *  Expand the STDERR handle as a resource.
 21866   */
 21867  static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData)
 21868  {
 21869  	jx9_vm *pVm = (jx9_vm *)pUserData;
 21870  	void *pResource;
 21871  	pResource = jx9ExportStderr(pVm);
 21872  	jx9_value_resource(pVal, pResource);
 21873  }
 21874  /*
 21875   * INI_SCANNER_NORMAL
 21876   *   Expand 1
 21877   */
 21878  static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData)
 21879  {
 21880  	SXUNUSED(pUserData); /* cc warning */
 21881  	jx9_value_int(pVal, 1);
 21882  }
 21883  /*
 21884   * INI_SCANNER_RAW
 21885   *   Expand 2
 21886   */
 21887  static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData)
 21888  {
 21889  	SXUNUSED(pUserData); /* cc warning */
 21890  	jx9_value_int(pVal, 2);
 21891  }
 21892  /*
 21893   * EXTR_OVERWRITE
 21894   *   Expand 0x01 (Must be a power of two)
 21895   */
 21896  static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData)
 21897  {
 21898  	SXUNUSED(pUserData); /* cc warning */
 21899  	jx9_value_int(pVal, 0x1);
 21900  }
 21901  /*
 21902   * EXTR_SKIP
 21903   *   Expand 0x02 (Must be a power of two)
 21904   */
 21905  static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData)
 21906  {
 21907  	SXUNUSED(pUserData); /* cc warning */
 21908  	jx9_value_int(pVal, 0x2);
 21909  }
 21910  /*
 21911   * EXTR_PREFIX_SAME
 21912   *   Expand 0x04 (Must be a power of two)
 21913   */
 21914  static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData)
 21915  {
 21916  	SXUNUSED(pUserData); /* cc warning */
 21917  	jx9_value_int(pVal, 0x4);
 21918  }
 21919  /*
 21920   * EXTR_PREFIX_ALL
 21921   *   Expand 0x08 (Must be a power of two)
 21922   */
 21923  static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData)
 21924  {
 21925  	SXUNUSED(pUserData); /* cc warning */
 21926  	jx9_value_int(pVal, 0x8);
 21927  }
 21928  /*
 21929   * EXTR_PREFIX_INVALID
 21930   *   Expand 0x10 (Must be a power of two)
 21931   */
 21932  static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData)
 21933  {
 21934  	SXUNUSED(pUserData); /* cc warning */
 21935  	jx9_value_int(pVal, 0x10);
 21936  }
 21937  /*
 21938   * EXTR_IF_EXISTS
 21939   *   Expand 0x20 (Must be a power of two)
 21940   */
 21941  static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
 21942  {
 21943  	SXUNUSED(pUserData); /* cc warning */
 21944  	jx9_value_int(pVal, 0x20);
 21945  }
 21946  /*
 21947   * EXTR_PREFIX_IF_EXISTS
 21948   *   Expand 0x40 (Must be a power of two)
 21949   */
 21950  static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
 21951  {
 21952  	SXUNUSED(pUserData); /* cc warning */
 21953  	jx9_value_int(pVal, 0x40);
 21954  }
 21955  /*
 21956   * Table of built-in constants.
 21957   */
 21958  static const jx9_builtin_constant aBuiltIn[] = {
 21959  	{"JX9_VERSION",          JX9_VER_Const      }, 
 21960  	{"JX9_ENGINE",           JX9_VER_Const      }, 
 21961  	{"__JX9__",              JX9_VER_Const      }, 
 21962  	{"JX9_OS",               JX9_OS_Const       }, 
 21963  	{"__OS__",               JX9_OS_Const       }, 
 21964  	{"JX9_EOL",              JX9_EOL_Const      }, 
 21965  	{"JX9_INT_MAX",          JX9_INTMAX_Const   }, 
 21966  	{"MAXINT",               JX9_INTMAX_Const   }, 
 21967  	{"JX9_INT_SIZE",         JX9_INTSIZE_Const  }, 
 21968  	{"PATH_SEPARATOR",       JX9_PATHSEP_Const  }, 
 21969  	{"DIRECTORY_SEPARATOR",  JX9_DIRSEP_Const   }, 
 21970  	{"DIR_SEP",              JX9_DIRSEP_Const   }, 
 21971  	{"__TIME__",             JX9_TIME_Const     }, 
 21972  	{"__DATE__",             JX9_DATE_Const     }, 
 21973  	{"__FILE__",             JX9_FILE_Const     }, 
 21974  	{"__DIR__",              JX9_DIR_Const      }, 
 21975  	{"E_ERROR",              JX9_E_ERROR_Const  }, 
 21976  	{"E_WARNING",            JX9_E_WARNING_Const}, 
 21977  	{"E_PARSE",              JX9_E_PARSE_Const  }, 
 21978  	{"E_NOTICE",             JX9_E_NOTICE_Const }, 
 21979  	{"CASE_LOWER",           JX9_CASE_LOWER_Const   }, 
 21980  	{"CASE_UPPER",           JX9_CASE_UPPER_Const   }, 
 21981  	{"STR_PAD_LEFT",         JX9_STR_PAD_LEFT_Const }, 
 21982  	{"STR_PAD_RIGHT",        JX9_STR_PAD_RIGHT_Const}, 
 21983  	{"STR_PAD_BOTH",         JX9_STR_PAD_BOTH_Const }, 
 21984  	{"COUNT_NORMAL",         JX9_COUNT_NORMAL_Const }, 
 21985  	{"COUNT_RECURSIVE",      JX9_COUNT_RECURSIVE_Const }, 
 21986  	{"SORT_ASC",             JX9_SORT_ASC_Const     }, 
 21987  	{"SORT_DESC",            JX9_SORT_DESC_Const    }, 
 21988  	{"SORT_REGULAR",         JX9_SORT_REG_Const     }, 
 21989  	{"SORT_NUMERIC",         JX9_SORT_NUMERIC_Const }, 
 21990  	{"SORT_STRING",          JX9_SORT_STRING_Const  }, 
 21991  	{"JX9_ROUND_HALF_DOWN",  JX9_JX9_ROUND_HALF_DOWN_Const }, 
 21992  	{"JX9_ROUND_HALF_EVEN",  JX9_JX9_ROUND_HALF_EVEN_Const }, 
 21993  	{"JX9_ROUND_HALF_UP",    JX9_JX9_ROUND_HALF_UP_Const   }, 
 21994  	{"JX9_ROUND_HALF_ODD",   JX9_JX9_ROUND_HALF_ODD_Const  }, 
 21995  #ifdef JX9_ENABLE_MATH_FUNC 
 21996  	{"PI",                 JX9_M_PI_Const         }, 
 21997  	{"M_E",                  JX9_M_E_Const          }, 
 21998  	{"M_LOG2E",              JX9_M_LOG2E_Const      }, 
 21999  	{"M_LOG10E",             JX9_M_LOG10E_Const     }, 
 22000  	{"M_LN2",                JX9_M_LN2_Const        }, 
 22001  	{"M_LN10",               JX9_M_LN10_Const       }, 
 22002  	{"M_PI_2",               JX9_M_PI_2_Const       }, 
 22003  	{"M_PI_4",               JX9_M_PI_4_Const       }, 
 22004  	{"M_1_PI",               JX9_M_1_PI_Const       }, 
 22005  	{"M_2_PI",               JX9_M_2_PI_Const       }, 
 22006  	{"M_SQRTPI",             JX9_M_SQRTPI_Const     }, 
 22007  	{"M_2_SQRTPI",           JX9_M_2_SQRTPI_Const   }, 
 22008  	{"M_SQRT2",              JX9_M_SQRT2_Const      }, 
 22009  	{"M_SQRT3",              JX9_M_SQRT3_Const      }, 
 22010  	{"M_SQRT1_2",            JX9_M_SQRT1_2_Const    }, 
 22011  	{"M_LNPI",               JX9_M_LNPI_Const       }, 
 22012  	{"M_EULER",              JX9_M_EULER_Const      }, 
 22013  #endif /* JX9_ENABLE_MATH_FUNC */
 22014  	{"DATE_ATOM",            JX9_DATE_ATOM_Const    }, 
 22015  	{"DATE_COOKIE",          JX9_DATE_COOKIE_Const  }, 
 22016  	{"DATE_ISO8601",         JX9_DATE_ISO8601_Const }, 
 22017  	{"DATE_RFC822",          JX9_DATE_RFC822_Const  }, 
 22018  	{"DATE_RFC850",          JX9_DATE_RFC850_Const  }, 
 22019  	{"DATE_RFC1036",         JX9_DATE_RFC1036_Const }, 
 22020  	{"DATE_RFC1123",         JX9_DATE_RFC1123_Const }, 
 22021  	{"DATE_RFC2822",         JX9_DATE_RFC2822_Const }, 
 22022  	{"DATE_RFC3339",         JX9_DATE_ATOM_Const    }, 
 22023  	{"DATE_RSS",             JX9_DATE_RSS_Const     }, 
 22024  	{"DATE_W3C",             JX9_DATE_W3C_Const     }, 
 22025  	{"ENT_COMPAT",           JX9_ENT_COMPAT_Const   }, 
 22026  	{"ENT_QUOTES",           JX9_ENT_QUOTES_Const   }, 
 22027  	{"ENT_NOQUOTES",         JX9_ENT_NOQUOTES_Const }, 
 22028  	{"ENT_IGNORE",           JX9_ENT_IGNORE_Const   }, 
 22029  	{"ENT_SUBSTITUTE",       JX9_ENT_SUBSTITUTE_Const}, 
 22030  	{"ENT_DISALLOWED",       JX9_ENT_DISALLOWED_Const}, 
 22031  	{"ENT_HTML401",          JX9_ENT_HTML401_Const  }, 
 22032  	{"ENT_XML1",             JX9_ENT_XML1_Const     }, 
 22033  	{"ENT_XHTML",            JX9_ENT_XHTML_Const    }, 
 22034  	{"ENT_HTML5",            JX9_ENT_HTML5_Const    }, 
 22035  	{"ISO-8859-1",           JX9_ISO88591_Const     }, 
 22036  	{"ISO_8859_1",           JX9_ISO88591_Const     }, 
 22037  	{"UTF-8",                JX9_UTF8_Const         }, 
 22038  	{"UTF8",                 JX9_UTF8_Const         }, 
 22039  	{"HTML_ENTITIES",        JX9_HTML_ENTITIES_Const}, 
 22040  	{"HTML_SPECIALCHARS",    JX9_HTML_SPECIALCHARS_Const }, 
 22041  	{"JX9_URL_SCHEME",       JX9_JX9_URL_SCHEME_Const}, 
 22042  	{"JX9_URL_HOST",         JX9_JX9_URL_HOST_Const}, 
 22043  	{"JX9_URL_PORT",         JX9_JX9_URL_PORT_Const}, 
 22044  	{"JX9_URL_USER",         JX9_JX9_URL_USER_Const}, 
 22045  	{"JX9_URL_PASS",         JX9_JX9_URL_PASS_Const}, 
 22046  	{"JX9_URL_PATH",         JX9_JX9_URL_PATH_Const}, 
 22047  	{"JX9_URL_QUERY",        JX9_JX9_URL_QUERY_Const}, 
 22048  	{"JX9_URL_FRAGMENT",     JX9_JX9_URL_FRAGMENT_Const}, 
 22049  	{"JX9_QUERY_RFC1738",    JX9_JX9_QUERY_RFC1738_Const}, 
 22050  	{"JX9_QUERY_RFC3986",    JX9_JX9_QUERY_RFC3986_Const}, 
 22051  	{"FNM_NOESCAPE",         JX9_FNM_NOESCAPE_Const }, 
 22052  	{"FNM_PATHNAME",         JX9_FNM_PATHNAME_Const }, 
 22053  	{"FNM_PERIOD",           JX9_FNM_PERIOD_Const   }, 
 22054  	{"FNM_CASEFOLD",         JX9_FNM_CASEFOLD_Const }, 
 22055  	{"PATHINFO_DIRNAME",     JX9_PATHINFO_DIRNAME_Const  }, 
 22056  	{"PATHINFO_BASENAME",    JX9_PATHINFO_BASENAME_Const }, 
 22057  	{"PATHINFO_EXTENSION",   JX9_PATHINFO_EXTENSION_Const}, 
 22058  	{"PATHINFO_FILENAME",    JX9_PATHINFO_FILENAME_Const }, 
 22059  	{"ASSERT_ACTIVE",        JX9_ASSERT_ACTIVE_Const     }, 
 22060  	{"ASSERT_WARNING",       JX9_ASSERT_WARNING_Const    }, 
 22061  	{"ASSERT_BAIL",          JX9_ASSERT_BAIL_Const       }, 
 22062  	{"ASSERT_QUIET_EVAL",    JX9_ASSERT_QUIET_EVAL_Const }, 
 22063  	{"ASSERT_CALLBACK",      JX9_ASSERT_CALLBACK_Const   }, 
 22064  	{"SEEK_SET",             JX9_SEEK_SET_Const      }, 
 22065  	{"SEEK_CUR",             JX9_SEEK_CUR_Const      }, 
 22066  	{"SEEK_END",             JX9_SEEK_END_Const      }, 
 22067  	{"LOCK_EX",              JX9_LOCK_EX_Const      }, 
 22068  	{"LOCK_SH",              JX9_LOCK_SH_Const      }, 
 22069  	{"LOCK_NB",              JX9_LOCK_NB_Const      }, 
 22070  	{"LOCK_UN",              JX9_LOCK_UN_Const      }, 
 22071  	{"FILE_USE_INC_PATH",    JX9_FILE_USE_INCLUDE_PATH_Const}, 
 22072  	{"FILE_IGN_NL",          JX9_FILE_IGNORE_NEW_LINES_Const}, 
 22073  	{"FILE_SKIP_EL",         JX9_FILE_SKIP_EMPTY_LINES_Const}, 
 22074  	{"FILE_APPEND",          JX9_FILE_APPEND_Const }, 
 22075  	{"SCANDIR_SORT_ASC",     JX9_SCANDIR_SORT_ASCENDING_Const  }, 
 22076  	{"SCANDIR_SORT_DESC",    JX9_SCANDIR_SORT_DESCENDING_Const }, 
 22077  	{"SCANDIR_SORT_NONE",    JX9_SCANDIR_SORT_NONE_Const }, 
 22078  	{"GLOB_MARK",            JX9_GLOB_MARK_Const    }, 
 22079  	{"GLOB_NOSORT",          JX9_GLOB_NOSORT_Const  }, 
 22080  	{"GLOB_NOCHECK",         JX9_GLOB_NOCHECK_Const }, 
 22081  	{"GLOB_NOESCAPE",        JX9_GLOB_NOESCAPE_Const}, 
 22082  	{"GLOB_BRACE",           JX9_GLOB_BRACE_Const   }, 
 22083  	{"GLOB_ONLYDIR",         JX9_GLOB_ONLYDIR_Const }, 
 22084  	{"GLOB_ERR",             JX9_GLOB_ERR_Const     }, 
 22085  	{"STDIN",                JX9_STDIN_Const        }, 
 22086  	{"stdin",                JX9_STDIN_Const        }, 
 22087  	{"STDOUT",               JX9_STDOUT_Const       }, 
 22088  	{"stdout",               JX9_STDOUT_Const       }, 
 22089  	{"STDERR",               JX9_STDERR_Const       }, 
 22090  	{"stderr",               JX9_STDERR_Const       }, 
 22091  	{"INI_SCANNER_NORMAL",   JX9_INI_SCANNER_NORMAL_Const }, 
 22092  	{"INI_SCANNER_RAW",      JX9_INI_SCANNER_RAW_Const    }, 
 22093  	{"EXTR_OVERWRITE",       JX9_EXTR_OVERWRITE_Const     }, 
 22094  	{"EXTR_SKIP",            JX9_EXTR_SKIP_Const        }, 
 22095  	{"EXTR_PREFIX_SAME",     JX9_EXTR_PREFIX_SAME_Const }, 
 22096  	{"EXTR_PREFIX_ALL",      JX9_EXTR_PREFIX_ALL_Const  }, 
 22097  	{"EXTR_PREFIX_INVALID",  JX9_EXTR_PREFIX_INVALID_Const }, 
 22098  	{"EXTR_IF_EXISTS",       JX9_EXTR_IF_EXISTS_Const   }, 
 22099  	{"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const}
 22100  };
 22101  /*
 22102   * Register the built-in constants defined above.
 22103   */
 22104  JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm)
 22105  {
 22106  	sxu32 n;
 22107  	/* 
 22108  	 * Note that all built-in constants have access to the jx9 virtual machine
 22109  	 * that trigger the constant invocation as their private data.
 22110  	 */
 22111  	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){
 22112  		jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm));
 22113  	}
 22114  }
 22115  /*
 22116   * ----------------------------------------------------------
 22117   * File: jx9_hashmap.c
 22118   * MD5: 4e93d15cd37e6093e25d8ede3064e210
 22119   * ----------------------------------------------------------
 22120   */
 22121  /*
 22122   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 22123   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 22124   * Version 1.7.2
 22125   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 22126   * please contact Symisc Systems via:
 22127   *       legal@symisc.net
 22128   *       licensing@symisc.net
 22129   *       contact@symisc.net
 22130   * or visit:
 22131   *      http://jx9.symisc.net/
 22132   */
 22133   /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable <chm@symisc.net> $ */
 22134  #ifndef JX9_AMALGAMATION
 22135  #include "jx9Int.h"
 22136  #endif
 22137  /* This file implement generic hashmaps used to represent JSON arrays and objects */
 22138  /* Allowed node types */
 22139  #define HASHMAP_INT_NODE   1  /* Node with an int [i.e: 64-bit integer] key */
 22140  #define HASHMAP_BLOB_NODE  2  /* Node with a string/BLOB key */
 22141  /*
 22142   * Default hash function for int [i.e; 64-bit integer] keys.
 22143   */
 22144  static sxu32 IntHash(sxi64 iKey)
 22145  {
 22146  	return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
 22147  }
 22148  /*
 22149   * Default hash function for string/BLOB keys.
 22150   */
 22151  static sxu32 BinHash(const void *pSrc, sxu32 nLen)
 22152  {
 22153  	register unsigned char *zIn = (unsigned char *)pSrc;
 22154  	unsigned char *zEnd;
 22155  	sxu32 nH = 5381;
 22156  	zEnd = &zIn[nLen];
 22157  	for(;;){
 22158  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 22159  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 22160  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 22161  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 22162  	}	
 22163  	return nH;
 22164  }
 22165  /*
 22166   * Return the total number of entries in a given hashmap.
 22167   * If bRecurisve is set to TRUE then recurse on hashmap entries.
 22168   * If the nesting limit is reached, this function abort immediately. 
 22169   */
 22170  static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount)
 22171  {
 22172  	sxi64 iCount = 0;
 22173  	if( !bRecursive ){
 22174  		iCount = pMap->nEntry;
 22175  	}else{
 22176  		/* Recursive hashmap walk */
 22177  		jx9_hashmap_node *pEntry = pMap->pLast;
 22178  		jx9_value *pElem;
 22179  		sxu32 n = 0;
 22180  		for(;;){
 22181  			if( n >= pMap->nEntry ){
 22182  				break;
 22183  			}
 22184  			/* Point to the element value */
 22185  			pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
 22186  			if( pElem ){
 22187  				if( pElem->iFlags & MEMOBJ_HASHMAP ){
 22188  					if( iRecCount > 31 ){
 22189  						/* Nesting limit reached */
 22190  						return iCount;
 22191  					}
 22192  					/* Recurse */
 22193  					iRecCount++;
 22194  					iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount);
 22195  					iRecCount--;
 22196  				}
 22197  			}
 22198  			/* Point to the next entry */
 22199  			pEntry = pEntry->pNext;
 22200  			++n;
 22201  		}
 22202  		/* Update count */
 22203  		iCount += pMap->nEntry;
 22204  	}
 22205  	return iCount;
 22206  }
 22207  /*
 22208   * Allocate a new hashmap node with a 64-bit integer key.
 22209   * If something goes wrong [i.e: out of memory], this function return NULL.
 22210   * Otherwise a fresh [jx9_hashmap_node] instance is returned.
 22211   */
 22212  static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx)
 22213  {
 22214  	jx9_hashmap_node *pNode;
 22215  	/* Allocate a new node */
 22216  	pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
 22217  	if( pNode == 0 ){
 22218  		return 0;
 22219  	}
 22220  	/* Zero the stucture */
 22221  	SyZero(pNode, sizeof(jx9_hashmap_node));
 22222  	/* Fill in the structure */
 22223  	pNode->pMap  = &(*pMap);
 22224  	pNode->iType = HASHMAP_INT_NODE;
 22225  	pNode->nHash = nHash;
 22226  	pNode->xKey.iKey = iKey;
 22227  	pNode->nValIdx  = nValIdx;
 22228  	return pNode;
 22229  }
 22230  /*
 22231   * Allocate a new hashmap node with a BLOB key.
 22232   * If something goes wrong [i.e: out of memory], this function return NULL.
 22233   * Otherwise a fresh [jx9_hashmap_node] instance is returned.
 22234   */
 22235  static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx)
 22236  {
 22237  	jx9_hashmap_node *pNode;
 22238  	/* Allocate a new node */
 22239  	pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
 22240  	if( pNode == 0 ){
 22241  		return 0;
 22242  	}
 22243  	/* Zero the stucture */
 22244  	SyZero(pNode, sizeof(jx9_hashmap_node));
 22245  	/* Fill in the structure */
 22246  	pNode->pMap  = &(*pMap);
 22247  	pNode->iType = HASHMAP_BLOB_NODE;
 22248  	pNode->nHash = nHash;
 22249  	SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
 22250  	SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
 22251  	pNode->nValIdx = nValIdx;
 22252  	return pNode;
 22253  }
 22254  /*
 22255   * link a hashmap node to the given bucket index (last argument to this function).
 22256   */
 22257  static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx)
 22258  {
 22259  	/* Link */
 22260  	if( pMap->apBucket[nBucketIdx] != 0 ){
 22261  		pNode->pNextCollide = pMap->apBucket[nBucketIdx];
 22262  		pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
 22263  	}
 22264  	pMap->apBucket[nBucketIdx] = pNode;
 22265  	/* Link to the map list */
 22266  	if( pMap->pFirst == 0 ){
 22267  		pMap->pFirst = pMap->pLast = pNode;
 22268  		/* Point to the first inserted node */
 22269  		pMap->pCur = pNode;
 22270  	}else{
 22271  		MACRO_LD_PUSH(pMap->pLast, pNode);
 22272  	}
 22273  	++pMap->nEntry;
 22274  }
 22275  /*
 22276   * Unlink a node from the hashmap.
 22277   * If the node count reaches zero then release the whole hash-bucket.
 22278   */
 22279  static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode)
 22280  {
 22281  	jx9_hashmap *pMap = pNode->pMap;
 22282  	jx9_vm *pVm = pMap->pVm;
 22283  	/* Unlink from the corresponding bucket */
 22284  	if( pNode->pPrevCollide == 0 ){
 22285  		pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
 22286  	}else{
 22287  		pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
 22288  	}
 22289  	if( pNode->pNextCollide ){
 22290  		pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
 22291  	}
 22292  	if( pMap->pFirst == pNode ){
 22293  		pMap->pFirst = pNode->pPrev;
 22294  	}
 22295  	if( pMap->pCur == pNode ){
 22296  		/* Advance the node cursor */
 22297  		pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
 22298  	}
 22299  	/* Unlink from the map list */
 22300  	MACRO_LD_REMOVE(pMap->pLast, pNode);
 22301  	/* Restore to the free list */
 22302  	jx9VmUnsetMemObj(pVm, pNode->nValIdx);	
 22303  	if( pNode->iType == HASHMAP_BLOB_NODE ){
 22304  		SyBlobRelease(&pNode->xKey.sKey);
 22305  	}
 22306  	SyMemBackendPoolFree(&pVm->sAllocator, pNode);
 22307  	pMap->nEntry--;
 22308  	if( pMap->nEntry < 1 ){
 22309  		/* Free the hash-bucket */
 22310  		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
 22311  		pMap->apBucket = 0;
 22312  		pMap->nSize = 0;
 22313  		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
 22314  	}
 22315  }
 22316  #define HASHMAP_FILL_FACTOR 3
 22317  /*
 22318   * Grow the hash-table and rehash all entries.
 22319   */
 22320  static sxi32 HashmapGrowBucket(jx9_hashmap *pMap)
 22321  {
 22322  	if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){
 22323  		jx9_hashmap_node **apOld = pMap->apBucket;
 22324  		jx9_hashmap_node *pEntry, **apNew;
 22325  		sxu32 nNew = pMap->nSize << 1;
 22326  		sxu32 nBucket;
 22327  		sxu32 n;
 22328  		if( nNew < 1 ){
 22329  			nNew = 16;
 22330  		}
 22331  		/* Allocate a new bucket */
 22332  		apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *));
 22333  		if( apNew == 0 ){
 22334  			if( pMap->nSize < 1 ){
 22335  				return SXERR_MEM; /* Fatal */
 22336  			}
 22337  			/* Not so fatal here, simply a performance hit */
 22338  			return SXRET_OK;
 22339  		}
 22340  		/* Zero the table */
 22341  		SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *));
 22342  		/* Reflect the change */
 22343  		pMap->apBucket = apNew;
 22344  		pMap->nSize = nNew;
 22345  		if( apOld == 0 ){
 22346  			/* First allocated table [i.e: no entry], return immediately */
 22347  			return SXRET_OK;
 22348  		}
 22349  		/* Rehash old entries */
 22350  		pEntry = pMap->pFirst;
 22351  		n = 0;
 22352  		for( ;; ){
 22353  			if( n >= pMap->nEntry ){
 22354  				break;
 22355  			}
 22356  			/* Clear the old collision link */
 22357  			pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 22358  			/* Link to the new bucket */
 22359  			nBucket = pEntry->nHash & (nNew - 1);
 22360  			if( pMap->apBucket[nBucket] != 0 ){
 22361  				pEntry->pNextCollide = pMap->apBucket[nBucket];
 22362  				pMap->apBucket[nBucket]->pPrevCollide = pEntry;
 22363  			}
 22364  			pMap->apBucket[nBucket] = pEntry;
 22365  			/* Point to the next entry */
 22366  			pEntry = pEntry->pPrev; /* Reverse link */
 22367  			n++;
 22368  		}
 22369  		/* Free the old table */
 22370  		SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
 22371  	}
 22372  	return SXRET_OK;
 22373  }
 22374  /*
 22375   * Insert a 64-bit integer key and it's associated value (if any) in the given
 22376   * hashmap.
 22377   */
 22378  static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue)
 22379  {
 22380  	jx9_hashmap_node *pNode;
 22381  	jx9_value *pObj;
 22382  	sxu32 nIdx;
 22383  	sxu32 nHash;
 22384  	sxi32 rc;
 22385  	/* Reserve a jx9_value for the value */
 22386  	pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
 22387  	if( pObj == 0 ){
 22388  		return SXERR_MEM;
 22389  	}
 22390  	if( pValue ){
 22391  		/* Duplicate the value */
 22392  		jx9MemObjStore(pValue, pObj);
 22393  	}	
 22394  	/* Hash the key */
 22395  	nHash = pMap->xIntHash(iKey);
 22396  	/* Allocate a new int node */
 22397  	pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
 22398  	if( pNode == 0 ){
 22399  		return SXERR_MEM;
 22400  	}
 22401  	/* Make sure the bucket is big enough to hold the new entry */
 22402  	rc = HashmapGrowBucket(&(*pMap));
 22403  	if( rc != SXRET_OK ){
 22404  		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
 22405  		return rc;
 22406  	}
 22407  	/* Perform the insertion */
 22408  	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
 22409  	/* All done */
 22410  	return SXRET_OK;
 22411  }
 22412  /*
 22413   * Insert a BLOB key and it's associated value (if any) in the given
 22414   * hashmap.
 22415   */
 22416  static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue)
 22417  {
 22418  	jx9_hashmap_node *pNode;
 22419  	jx9_value *pObj;
 22420  	sxu32 nHash;
 22421  	sxu32 nIdx;
 22422  	sxi32 rc;
 22423  	/* Reserve a jx9_value for the value */
 22424  	pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
 22425  	if( pObj == 0 ){
 22426  		return SXERR_MEM;
 22427  	}
 22428  	if( pValue ){
 22429  		/* Duplicate the value */
 22430  		jx9MemObjStore(pValue, pObj);
 22431  	}
 22432  	/* Hash the key */
 22433  	nHash = pMap->xBlobHash(pKey, nKeyLen);
 22434  	/* Allocate a new blob node */
 22435  	pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
 22436  	if( pNode == 0 ){
 22437  		return SXERR_MEM;
 22438  	}
 22439  	/* Make sure the bucket is big enough to hold the new entry */
 22440  	rc = HashmapGrowBucket(&(*pMap));
 22441  	if( rc != SXRET_OK ){
 22442  		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
 22443  		return rc;
 22444  	}
 22445  	/* Perform the insertion */
 22446  	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
 22447  	/* All done */
 22448  	return SXRET_OK;
 22449  }
 22450  /*
 22451   * Check if a given 64-bit integer key exists in the given hashmap.
 22452   * Write a pointer to the target node on success. Otherwise
 22453   * SXERR_NOTFOUND is returned on failure.
 22454   */
 22455  static sxi32 HashmapLookupIntKey(
 22456  	jx9_hashmap *pMap,         /* Target hashmap */
 22457  	sxi64 iKey,                /* lookup key */
 22458  	jx9_hashmap_node **ppNode  /* OUT: target node on success */
 22459  	)
 22460  {
 22461  	jx9_hashmap_node *pNode;
 22462  	sxu32 nHash;
 22463  	if( pMap->nEntry < 1 ){
 22464  		/* Don't bother hashing, there is no entry anyway */
 22465  		return SXERR_NOTFOUND;
 22466  	}
 22467  	/* Hash the key first */
 22468  	nHash = pMap->xIntHash(iKey);
 22469  	/* Point to the appropriate bucket */
 22470  	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
 22471  	/* Perform the lookup */
 22472  	for(;;){
 22473  		if( pNode == 0 ){
 22474  			break;
 22475  		}
 22476  		if( pNode->iType == HASHMAP_INT_NODE
 22477  			&& pNode->nHash == nHash
 22478  			&& pNode->xKey.iKey == iKey ){
 22479  				/* Node found */
 22480  				if( ppNode ){
 22481  					*ppNode = pNode;
 22482  				}
 22483  				return SXRET_OK;
 22484  		}
 22485  		/* Follow the collision link */
 22486  		pNode = pNode->pNextCollide;
 22487  	}
 22488  	/* No such entry */
 22489  	return SXERR_NOTFOUND;
 22490  }
 22491  /*
 22492   * Check if a given BLOB key exists in the given hashmap.
 22493   * Write a pointer to the target node on success. Otherwise
 22494   * SXERR_NOTFOUND is returned on failure.
 22495   */
 22496  static sxi32 HashmapLookupBlobKey(
 22497  	jx9_hashmap *pMap,          /* Target hashmap */
 22498  	const void *pKey,           /* Lookup key */
 22499  	sxu32 nKeyLen,              /* Key length in bytes */
 22500  	jx9_hashmap_node **ppNode   /* OUT: target node on success */
 22501  	)
 22502  {
 22503  	jx9_hashmap_node *pNode;
 22504  	sxu32 nHash;
 22505  	if( pMap->nEntry < 1 ){
 22506  		/* Don't bother hashing, there is no entry anyway */
 22507  		return SXERR_NOTFOUND;
 22508  	}
 22509  	/* Hash the key first */
 22510  	nHash = pMap->xBlobHash(pKey, nKeyLen);
 22511  	/* Point to the appropriate bucket */
 22512  	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
 22513  	/* Perform the lookup */
 22514  	for(;;){
 22515  		if( pNode == 0 ){
 22516  			break;
 22517  		}
 22518  		if( pNode->iType == HASHMAP_BLOB_NODE 
 22519  			&& pNode->nHash == nHash
 22520  			&& SyBlobLength(&pNode->xKey.sKey) == nKeyLen 
 22521  			&& SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){
 22522  				/* Node found */
 22523  				if( ppNode ){
 22524  					*ppNode = pNode;
 22525  				}
 22526  				return SXRET_OK;
 22527  		}
 22528  		/* Follow the collision link */
 22529  		pNode = pNode->pNextCollide;
 22530  	}
 22531  	/* No such entry */
 22532  	return SXERR_NOTFOUND;
 22533  }
 22534  /*
 22535   * Check if the given BLOB key looks like a decimal number. 
 22536   * Retrurn TRUE on success.FALSE otherwise.
 22537   */
 22538  static int HashmapIsIntKey(SyBlob *pKey)
 22539  {
 22540  	const char *zIn  = (const char *)SyBlobData(pKey);
 22541  	const char *zEnd = &zIn[SyBlobLength(pKey)];
 22542  	if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){
 22543  		/* Octal not decimal number */
 22544  		return FALSE;
 22545  	}
 22546  	if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){
 22547  		zIn++;
 22548  	}
 22549  	for(;;){
 22550  		if( zIn >= zEnd ){
 22551  			return TRUE;
 22552  		}
 22553  		if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */  || !SyisDigit(zIn[0]) ){
 22554  			break;
 22555  		}
 22556  		zIn++;
 22557  	}
 22558  	/* Key does not look like a decimal number */
 22559  	return FALSE;
 22560  }
 22561  /*
 22562   * Check if a given key exists in the given hashmap.
 22563   * Write a pointer to the target node on success.
 22564   * Otherwise SXERR_NOTFOUND is returned on failure.
 22565   */
 22566  static sxi32 HashmapLookup(
 22567  	jx9_hashmap *pMap,          /* Target hashmap */
 22568  	jx9_value *pKey,            /* Lookup key */
 22569  	jx9_hashmap_node **ppNode   /* OUT: target node on success */
 22570  	)
 22571  {
 22572  	jx9_hashmap_node *pNode = 0; /* cc -O6 warning */
 22573  	sxi32 rc;
 22574  	if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
 22575  		if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
 22576  			/* Force a string cast */
 22577  			jx9MemObjToString(&(*pKey));
 22578  		}
 22579  		if( SyBlobLength(&pKey->sBlob) > 0 ){
 22580  			/* Perform a blob lookup */
 22581  			rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
 22582  			goto result;
 22583  		}
 22584  	}
 22585  	/* Perform an int lookup */
 22586  	if((pKey->iFlags & MEMOBJ_INT) == 0 ){
 22587  		/* Force an integer cast */
 22588  		jx9MemObjToInteger(pKey);
 22589  	}
 22590  	/* Perform an int lookup */
 22591  	rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
 22592  result:
 22593  	if( rc == SXRET_OK ){
 22594  		/* Node found */
 22595  		if( ppNode ){
 22596  			*ppNode = pNode;
 22597  		}
 22598  		return SXRET_OK;
 22599  	}
 22600  	/* No such entry */
 22601  	return SXERR_NOTFOUND;
 22602  }
 22603  /*
 22604   * Insert a given key and it's associated value (if any) in the given
 22605   * hashmap.
 22606   * If a node with the given key already exists in the database
 22607   * then this function overwrite the old value.
 22608   */
 22609  static sxi32 HashmapInsert(
 22610  	jx9_hashmap *pMap, /* Target hashmap */
 22611  	jx9_value *pKey,   /* Lookup key  */
 22612  	jx9_value *pVal    /* Node value */
 22613  	)
 22614  {
 22615  	jx9_hashmap_node *pNode = 0;
 22616  	sxi32 rc = SXRET_OK;
 22617  	if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){
 22618  		pMap->iFlags |= HASHMAP_JSON_OBJECT;
 22619  	}
 22620  	if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){
 22621  		if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
 22622  			/* Force a string cast */
 22623  			jx9MemObjToString(&(*pKey));
 22624  		}
 22625  		if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){
 22626  			if(SyBlobLength(&pKey->sBlob) < 1){
 22627  				/* Automatic index assign */
 22628  				pKey = 0;
 22629  			}
 22630  			goto IntKey;
 22631  		}
 22632  		if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), 
 22633  			SyBlobLength(&pKey->sBlob), &pNode) ){
 22634  				/* Overwrite the old value */
 22635  				jx9_value *pElem;
 22636  				pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
 22637  				if( pElem ){
 22638  					if( pVal ){
 22639  						jx9MemObjStore(pVal, pElem);
 22640  					}else{
 22641  						/* Nullify the entry */
 22642  						jx9MemObjToNull(pElem);
 22643  					}
 22644  				}
 22645  				return SXRET_OK;
 22646  		}
 22647  		/* Perform a blob-key insertion */
 22648  		rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal));
 22649  		return rc;
 22650  	}
 22651  IntKey:
 22652  	if( pKey ){
 22653  		if((pKey->iFlags & MEMOBJ_INT) == 0 ){
 22654  			/* Force an integer cast */
 22655  			jx9MemObjToInteger(pKey);
 22656  		}
 22657  		if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){
 22658  			/* Overwrite the old value */
 22659  			jx9_value *pElem;
 22660  			pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
 22661  			if( pElem ){
 22662  				if( pVal ){
 22663  					jx9MemObjStore(pVal, pElem);
 22664  				}else{
 22665  					/* Nullify the entry */
 22666  					jx9MemObjToNull(pElem);
 22667  				}
 22668  			}
 22669  			return SXRET_OK;
 22670  		}
 22671  		/* Perform a 64-bit-int-key insertion */
 22672  		rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal));
 22673  		if( rc == SXRET_OK ){
 22674  			if( pKey->x.iVal >= pMap->iNextIdx ){
 22675  				/* Increment the automatic index */ 
 22676  				pMap->iNextIdx = pKey->x.iVal + 1;
 22677  				/* Make sure the automatic index is not reserved */
 22678  				while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){
 22679  					pMap->iNextIdx++;
 22680  				}
 22681  			}
 22682  		}
 22683  	}else{
 22684  		/* Assign an automatic index */
 22685  		rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal));
 22686  		if( rc == SXRET_OK ){
 22687  			++pMap->iNextIdx;
 22688  		}
 22689  	}
 22690  	/* Insertion result */
 22691  	return rc;
 22692  }
 22693  /*
 22694   * Extract node value.
 22695   */
 22696  static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode)
 22697  {
 22698  	/* Point to the desired object */
 22699  	jx9_value *pObj;
 22700  	pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
 22701  	return pObj;
 22702  }
 22703  /*
 22704   * Insert a node in the given hashmap.
 22705   * If a node with the given key already exists in the database
 22706   * then this function overwrite the old value.
 22707   */
 22708  static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve)
 22709  {
 22710  	jx9_value *pObj;
 22711  	sxi32 rc;
 22712  	/* Extract the node value */
 22713  	pObj = HashmapExtractNodeValue(&(*pNode));
 22714  	if( pObj == 0 ){
 22715  		return SXERR_EMPTY;
 22716  	}
 22717  	/* Preserve key */
 22718  	if( pNode->iType == HASHMAP_INT_NODE){
 22719  		/* Int64 key */
 22720  		if( !bPreserve ){
 22721  			/* Assign an automatic index */
 22722  			rc = HashmapInsert(&(*pMap), 0, pObj);
 22723  		}else{
 22724  			rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj);
 22725  		}
 22726  	}else{
 22727  		/* Blob key */
 22728  		rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey), 
 22729  			SyBlobLength(&pNode->xKey.sKey), pObj);
 22730  	}
 22731  	return rc;
 22732  }
 22733  /*
 22734   * Compare two node values.
 22735   * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
 22736   * or < 0 if pRight is greater than pLeft.
 22737   * For a full description on jx9_values comparison, refer to the implementation
 22738   * of the [jx9MemObjCmp()] function defined in memobj.c or the official
 22739   * documenation.
 22740   */
 22741  static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict)
 22742  {
 22743  	jx9_value sObj1, sObj2;
 22744  	sxi32 rc;
 22745  	if( pLeft == pRight ){
 22746  		/*
 22747  		 * Same node.Refer to the sort() implementation defined
 22748  		 * below for more information on this sceanario.
 22749  		 */
 22750  		return 0;
 22751  	}
 22752  	/* Do the comparison */
 22753  	jx9MemObjInit(pLeft->pMap->pVm, &sObj1);
 22754  	jx9MemObjInit(pLeft->pMap->pVm, &sObj2);
 22755  	jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
 22756  	jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE);
 22757  	rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0);
 22758  	jx9MemObjRelease(&sObj1);
 22759  	jx9MemObjRelease(&sObj2);
 22760  	return rc;
 22761  }
 22762  /*
 22763   * Rehash a node with a 64-bit integer key.
 22764   * Refer to [merge_sort(), array_shift()] implementations for more information.
 22765   */
 22766  static void HashmapRehashIntNode(jx9_hashmap_node *pEntry)
 22767  {
 22768  	jx9_hashmap *pMap = pEntry->pMap;
 22769  	sxu32 nBucket;
 22770  	/* Remove old collision links */
 22771  	if( pEntry->pPrevCollide ){
 22772  		pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
 22773  	}else{
 22774  		pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
 22775  	}
 22776  	if( pEntry->pNextCollide ){
 22777  		pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
 22778  	}
 22779  	pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 22780  	/* Compute the new hash */
 22781  	pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
 22782  	pEntry->xKey.iKey = pMap->iNextIdx;
 22783  	nBucket = pEntry->nHash & (pMap->nSize - 1);
 22784  	/* Link to the new bucket */
 22785  	pEntry->pNextCollide = pMap->apBucket[nBucket];
 22786  	if( pMap->apBucket[nBucket] ){
 22787  		pMap->apBucket[nBucket]->pPrevCollide = pEntry;
 22788  	}
 22789  	pEntry->pNextCollide = pMap->apBucket[nBucket];
 22790  	pMap->apBucket[nBucket] = pEntry;
 22791  	/* Increment the automatic index */
 22792  	pMap->iNextIdx++;
 22793  }
 22794  /*
 22795   * Perform a linear search on a given hashmap.
 22796   * Write a pointer to the target node on success.
 22797   * Otherwise SXERR_NOTFOUND is returned on failure.
 22798   * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations 
 22799   * for more information.
 22800   */
 22801  static int HashmapFindValue(
 22802  	jx9_hashmap *pMap,   /* Target hashmap */
 22803  	jx9_value *pNeedle,  /* Lookup key */
 22804  	jx9_hashmap_node **ppNode, /* OUT: target node on success  */
 22805  	int bStrict      /* TRUE for strict comparison */
 22806  	)
 22807  {
 22808  	jx9_hashmap_node *pEntry;
 22809  	jx9_value sVal, *pVal;
 22810  	jx9_value sNeedle;
 22811  	sxi32 rc;
 22812  	sxu32 n;
 22813  	/* Perform a linear search since we cannot sort the hashmap based on values */
 22814  	pEntry = pMap->pFirst;
 22815  	n = pMap->nEntry;
 22816  	jx9MemObjInit(pMap->pVm, &sVal);
 22817  	jx9MemObjInit(pMap->pVm, &sNeedle);
 22818  	for(;;){
 22819  		if( n < 1 ){
 22820  			break;
 22821  		}
 22822  		/* Extract node value */
 22823  		pVal = HashmapExtractNodeValue(pEntry);
 22824  		if( pVal ){
 22825  			if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){
 22826  				sxi32 iF1 = pVal->iFlags;
 22827  				sxi32 iF2 = pNeedle->iFlags;
 22828  				if( iF1 == iF2 ){
 22829  					/* NULL values are equals */
 22830  					if( ppNode ){
 22831  						*ppNode = pEntry;
 22832  					}
 22833  					return SXRET_OK;
 22834  				}
 22835  			}else{
 22836  				/* Duplicate value */
 22837  				jx9MemObjLoad(pVal, &sVal);
 22838  				jx9MemObjLoad(pNeedle, &sNeedle);
 22839  				rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0);
 22840  				jx9MemObjRelease(&sVal);
 22841  				jx9MemObjRelease(&sNeedle);
 22842  				if( rc == 0 ){
 22843  					if( ppNode ){
 22844  						*ppNode = pEntry;
 22845  					}
 22846  					/* Match found*/
 22847  					return SXRET_OK;
 22848  				}
 22849  			}
 22850  		}
 22851  		/* Point to the next entry */
 22852  		pEntry = pEntry->pPrev; /* Reverse link */
 22853  		n--;
 22854  	}
 22855  	/* No such entry */
 22856  	return SXERR_NOTFOUND;
 22857  }
 22858  /*
 22859   * Compare two hashmaps.
 22860   * Return 0 if the hashmaps are equals.Any other value indicates inequality.
 22861   * Note on array comparison operators.
 22862   *  According to the JX9 language reference manual.
 22863   *  Array Operators Example 	Name 	Result
 22864   *  $a + $b 	Union 	Union of $a and $b.
 22865   *  $a == $b 	Equality 	TRUE if $a and $b have the same key/value pairs.
 22866   *  $a === $b 	Identity 	TRUE if $a and $b have the same key/value pairs in the same 
 22867   *                          order and of the same types.
 22868   *  $a != $b 	Inequality 	TRUE if $a is not equal to $b.
 22869   *  $a <> $b 	Inequality 	TRUE if $a is not equal to $b.
 22870   *  $a !== $b 	Non-identity 	TRUE if $a is not identical to $b.
 22871   * The + operator returns the right-hand array appended to the left-hand array;
 22872   * For keys that exist in both arrays, the elements from the left-hand array will be used
 22873   * and the matching elements from the right-hand array will be ignored.
 22874   * <?jx9
 22875   * $a = array("a" => "apple", "b" => "banana");
 22876   * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
 22877   * $c = $a + $b; // Union of $a and $b
 22878   * print "Union of \$a and \$b: \n";
 22879   * dump($c);
 22880   * $c = $b + $a; // Union of $b and $a
 22881   * print "Union of \$b and \$a: \n";
 22882   * dump($c);
 22883   * ?>
 22884   * When executed, this script will print the following:
 22885   * Union of $a and $b:
 22886   * array(3) {
 22887   *  ["a"]=>
 22888   *  string(5) "apple"
 22889   *  ["b"]=>
 22890   * string(6) "banana"
 22891   *  ["c"]=>
 22892   * string(6) "cherry"
 22893   * }
 22894   * Union of $b and $a:
 22895   * array(3) {
 22896   * ["a"]=>
 22897   * string(4) "pear"
 22898   * ["b"]=>
 22899   * string(10) "strawberry"
 22900   * ["c"]=>
 22901   * string(6) "cherry"
 22902   * }
 22903   * Elements of arrays are equal for the comparison if they have the same key and value.
 22904   */
 22905  JX9_PRIVATE sxi32 jx9HashmapCmp(
 22906  	jx9_hashmap *pLeft,  /* Left hashmap */
 22907  	jx9_hashmap *pRight, /* Right hashmap */
 22908  	int bStrict          /* TRUE for strict comparison */
 22909  	)
 22910  {
 22911  	jx9_hashmap_node *pLe, *pRe;
 22912  	sxi32 rc;
 22913  	sxu32 n;
 22914  	if( pLeft == pRight ){
 22915  		/* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
 22916  		 * Unlike the  engine.
 22917  		 */
 22918  		return 0;
 22919  	}
 22920  	if( pLeft->nEntry != pRight->nEntry ){
 22921  		/* Must have the same number of entries */
 22922  		return pLeft->nEntry > pRight->nEntry ? 1 : -1;
 22923  	}
 22924  	/* Point to the first inserted entry of the left hashmap */
 22925  	pLe = pLeft->pFirst;
 22926  	pRe = 0; /* cc warning */
 22927  	/* Perform the comparison */
 22928  	n = pLeft->nEntry;
 22929  	for(;;){
 22930  		if( n < 1 ){
 22931  			break;
 22932  		}
 22933  		if( pLe->iType == HASHMAP_INT_NODE){
 22934  			/* Int key */
 22935  			rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
 22936  		}else{
 22937  			SyBlob *pKey = &pLe->xKey.sKey;
 22938  			/* Blob key */
 22939  			rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
 22940  		}
 22941  		if( rc != SXRET_OK ){
 22942  			/* No such entry in the right side */
 22943  			return 1;
 22944  		}
 22945  		rc = 0;
 22946  		if( bStrict ){
 22947  			/* Make sure, the keys are of the same type */
 22948  			if( pLe->iType != pRe->iType ){
 22949  				rc = 1;
 22950  			}
 22951  		}
 22952  		if( !rc ){
 22953  			/* Compare nodes */
 22954  			rc = HashmapNodeCmp(pLe, pRe, bStrict);
 22955  		}
 22956  		if( rc != 0 ){
 22957  			/* Nodes key/value differ */
 22958  			return rc;
 22959  		}
 22960  		/* Point to the next entry */
 22961  		pLe = pLe->pPrev; /* Reverse link */
 22962  		n--;
 22963  	}
 22964  	return 0; /* Hashmaps are equals */
 22965  }
 22966  /*
 22967   * Merge two hashmaps.
 22968   * Note on the merge process
 22969   * According to the JX9 language reference manual.
 22970   *  Merges the elements of two arrays together so that the values of one are appended
 22971   *  to the end of the previous one. It returns the resulting array (pDest).
 22972   *  If the input arrays have the same string keys, then the later value for that key
 22973   *  will overwrite the previous one. If, however, the arrays contain numeric keys
 22974   *  the later value will not overwrite the original value, but will be appended.
 22975   *  Values in the input array with numeric keys will be renumbered with incrementing
 22976   *  keys starting from zero in the result array. 
 22977   */
 22978  static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest)
 22979  {
 22980  	jx9_hashmap_node *pEntry;
 22981  	jx9_value sKey, *pVal;
 22982  	sxi32 rc;
 22983  	sxu32 n;
 22984  	if( pSrc == pDest ){
 22985  		/* Same map. This can easily happen since hashmaps are passed by reference.
 22986  		 * Unlike the  engine.
 22987  		 */
 22988  		return SXRET_OK;
 22989  	}
 22990  	/* Point to the first inserted entry in the source */
 22991  	pEntry = pSrc->pFirst;
 22992  	/* Perform the merge */
 22993  	for( n = 0 ; n < pSrc->nEntry ; ++n ){
 22994  		/* Extract the node value */
 22995  		pVal = HashmapExtractNodeValue(pEntry);
 22996  		if( pEntry->iType == HASHMAP_BLOB_NODE ){
 22997  			/* Blob key insertion */
 22998  			jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
 22999  			jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 23000  			rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
 23001  			jx9MemObjRelease(&sKey);
 23002  		}else{
 23003  			rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
 23004  		}
 23005  		if( rc != SXRET_OK ){
 23006  			return rc;
 23007  		}
 23008  		/* Point to the next entry */
 23009  		pEntry = pEntry->pPrev; /* Reverse link */
 23010  	}
 23011  	return SXRET_OK;
 23012  }
 23013  /*
 23014   * Duplicate the contents of a hashmap. Store the copy in pDest.
 23015   * Refer to the [array_pad(), array_copy(), ...] implementation for more information.
 23016   */
 23017  JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest)
 23018  {
 23019  	jx9_hashmap_node *pEntry;
 23020  	jx9_value sKey, *pVal;
 23021  	sxi32 rc;
 23022  	sxu32 n;
 23023  	if( pSrc == pDest ){
 23024  		/* Same map. This can easily happen since hashmaps are passed by reference.
 23025  		 * Unlike the  engine.
 23026  		 */
 23027  		return SXRET_OK;
 23028  	}
 23029  	/* Point to the first inserted entry in the source */
 23030  	pEntry = pSrc->pFirst;
 23031  	/* Perform the duplication */
 23032  	for( n = 0 ; n < pSrc->nEntry ; ++n ){
 23033  		/* Extract the node value */
 23034  		pVal = HashmapExtractNodeValue(pEntry);
 23035  		if( pEntry->iType == HASHMAP_BLOB_NODE ){
 23036  			/* Blob key insertion */
 23037  			jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
 23038  			jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 23039  			rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
 23040  			jx9MemObjRelease(&sKey);
 23041  		}else{
 23042  			/* Int key insertion */
 23043  			rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal);
 23044  		}
 23045  		if( rc != SXRET_OK ){
 23046  			return rc;
 23047  		}
 23048  		/* Point to the next entry */
 23049  		pEntry = pEntry->pPrev; /* Reverse link */
 23050  	}
 23051  	return SXRET_OK;
 23052  }
 23053  /*
 23054   * Perform the union of two hashmaps.
 23055   * This operation is performed only if the user uses the '+' operator
 23056   * with a variable holding an array as follows:
 23057   * <?jx9
 23058   * $a = array("a" => "apple", "b" => "banana");
 23059   * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
 23060   * $c = $a + $b; // Union of $a and $b
 23061   * print "Union of \$a and \$b: \n";
 23062   * dump($c);
 23063   * $c = $b + $a; // Union of $b and $a
 23064   * print "Union of \$b and \$a: \n";
 23065   * dump($c);
 23066   * ?>
 23067   * When executed, this script will print the following:
 23068   * Union of $a and $b:
 23069   * array(3) {
 23070   *  ["a"]=>
 23071   *  string(5) "apple"
 23072   *  ["b"]=>
 23073   * string(6) "banana"
 23074   *  ["c"]=>
 23075   * string(6) "cherry"
 23076   * }
 23077   * Union of $b and $a:
 23078   * array(3) {
 23079   * ["a"]=>
 23080   * string(4) "pear"
 23081   * ["b"]=>
 23082   * string(10) "strawberry"
 23083   * ["c"]=>
 23084   * string(6) "cherry"
 23085   * }
 23086   * The + operator returns the right-hand array appended to the left-hand array;
 23087   * For keys that exist in both arrays, the elements from the left-hand array will be used
 23088   * and the matching elements from the right-hand array will be ignored.
 23089   */
 23090  JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight)
 23091  {
 23092  	jx9_hashmap_node *pEntry;
 23093  	sxi32 rc = SXRET_OK;
 23094  	jx9_value *pObj;
 23095  	sxu32 n;
 23096  	if( pLeft == pRight ){
 23097  		/* Same map. This can easily happen since hashmaps are passed by reference.
 23098  		 * Unlike the  engine.
 23099  		 */
 23100  		return SXRET_OK;
 23101  	}
 23102  	/* Perform the union */
 23103  	pEntry = pRight->pFirst;
 23104  	for(n = 0 ; n < pRight->nEntry ; ++n ){
 23105  		/* Make sure the given key does not exists in the left array */
 23106  		if( pEntry->iType == HASHMAP_BLOB_NODE ){
 23107  			/* BLOB key */
 23108  			if( SXRET_OK != 
 23109  				HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){
 23110  					pObj = HashmapExtractNodeValue(pEntry);
 23111  					if( pObj ){
 23112  						/* Perform the insertion */
 23113  						rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey),
 23114  							SyBlobLength(&pEntry->xKey.sKey),pObj);
 23115  						if( rc != SXRET_OK ){
 23116  							return rc;
 23117  						}
 23118  					}
 23119  			}
 23120  		}else{
 23121  			/* INT key */
 23122  			if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){
 23123  				pObj = HashmapExtractNodeValue(pEntry);
 23124  				if( pObj ){
 23125  					/* Perform the insertion */
 23126  					rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj);
 23127  					if( rc != SXRET_OK ){
 23128  						return rc;
 23129  					}
 23130  				}
 23131  			}
 23132  		}
 23133  		/* Point to the next entry */
 23134  		pEntry = pEntry->pPrev; /* Reverse link */
 23135  	}
 23136  	return SXRET_OK;
 23137  }
 23138  /*
 23139   * Allocate a new hashmap.
 23140   * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
 23141   */
 23142  JX9_PRIVATE jx9_hashmap * jx9NewHashmap(
 23143  	jx9_vm *pVm,              /* VM that trigger the hashmap creation */
 23144  	sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/
 23145  	sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
 23146  	)
 23147  {
 23148  	jx9_hashmap *pMap;
 23149  	/* Allocate a new instance */
 23150  	pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap));
 23151  	if( pMap == 0 ){
 23152  		return 0;
 23153  	}
 23154  	/* Zero the structure */
 23155  	SyZero(pMap, sizeof(jx9_hashmap));
 23156  	/* Fill in the structure */
 23157  	pMap->pVm = &(*pVm);
 23158  	pMap->iRef = 1;
 23159  	/* pMap->iFlags = 0; */
 23160  	/* Default hash functions */
 23161  	pMap->xIntHash  = xIntHash ? xIntHash : IntHash;
 23162  	pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
 23163  	return pMap;
 23164  }
 23165  /*
 23166   * Install superglobals in the given virtual machine.
 23167   * Note on superglobals.
 23168   *  According to the JX9 language reference manual.
 23169   *  Superglobals are built-in variables that are always available in all scopes.
 23170  *   Description
 23171  *   All predefined variables in JX9 are "superglobals", which means they
 23172  *   are available in all scopes throughout a script.
 23173  *   These variables are:
 23174  *    $_SERVER
 23175  *    $_GET
 23176  *    $_POST
 23177  *    $_FILES
 23178  *    $_REQUEST
 23179  *    $_ENV
 23180  */
 23181  JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm)
 23182  {
 23183  	static const char * azSuper[] = {
 23184  		"_SERVER",   /* $_SERVER */
 23185  		"_GET",      /* $_GET */
 23186  		"_POST",     /* $_POST */
 23187  		"_FILES",    /* $_FILES */
 23188  		"_REQUEST",  /* $_REQUEST */
 23189  		"_COOKIE",   /* $_COOKIE */
 23190  		"_ENV",      /* $_ENV */
 23191  		"_HEADER",   /* $_HEADER */
 23192  		"argv"       /* $argv */
 23193  	};
 23194  	SyString *pFile;
 23195  	sxi32 rc;
 23196  	sxu32 n;
 23197  	/* Install globals variable now */
 23198  	for( n =  0 ; n < SX_ARRAYSIZE(azSuper)  ; n++ ){
 23199  		jx9_value *pSuper;
 23200  		/* Request an empty array */
 23201  		pSuper = jx9_new_array(&(*pVm));
 23202  		if( pSuper == 0 ){
 23203  			return SXERR_MEM;
 23204  		}
 23205  		/* Install */
 23206  		rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
 23207  		if( rc != SXRET_OK ){
 23208  			return rc;
 23209  		}
 23210  		/* Release the value now it have been installed */
 23211  		jx9_release_value(&(*pVm), pSuper);
 23212  	}
 23213  	/* Set some $_SERVER entries */
 23214  	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 23215  	/*
 23216  	 * 'SCRIPT_FILENAME'
 23217  	 * The absolute pathname of the currently executing script.
 23218  	 */
 23219  	jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR, 
 23220  		"SCRIPT_FILENAME", 
 23221  		pFile ? pFile->zString : ":Memory:", 
 23222  		pFile ? pFile->nByte : sizeof(":Memory:") - 1
 23223  		);
 23224  	/* All done, all global variables are installed now */
 23225  	return SXRET_OK;
 23226  }
 23227  /*
 23228   * Release a hashmap.
 23229   */
 23230  JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS)
 23231  {
 23232  	jx9_hashmap_node *pEntry, *pNext;
 23233  	jx9_vm *pVm = pMap->pVm;
 23234  	sxu32 n;
 23235  	/* Start the release process */
 23236  	n = 0;
 23237  	pEntry = pMap->pFirst;
 23238  	for(;;){
 23239  		if( n >= pMap->nEntry ){
 23240  			break;
 23241  		}
 23242  		pNext = pEntry->pPrev; /* Reverse link */
 23243  		/* Restore the jx9_value to the free list */
 23244  		jx9VmUnsetMemObj(pVm, pEntry->nValIdx);
 23245  		/* Release the node */
 23246  		if( pEntry->iType == HASHMAP_BLOB_NODE ){
 23247  			SyBlobRelease(&pEntry->xKey.sKey);
 23248  		}
 23249  		SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
 23250  		/* Point to the next entry */
 23251  		pEntry = pNext;
 23252  		n++;
 23253  	}
 23254  	if( pMap->nEntry > 0 ){
 23255  		/* Release the hash bucket */
 23256  		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
 23257  	}
 23258  	if( FreeDS ){
 23259  		/* Free the whole instance */
 23260  		SyMemBackendPoolFree(&pVm->sAllocator, pMap);
 23261  	}else{
 23262  		/* Keep the instance but reset it's fields */
 23263  		pMap->apBucket = 0;
 23264  		pMap->iNextIdx = 0;
 23265  		pMap->nEntry = pMap->nSize = 0;
 23266  		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
 23267  	}
 23268  	return SXRET_OK;
 23269  }
 23270  /*
 23271   * Decrement the reference count of a given hashmap.
 23272   * If the count reaches zero which mean no more variables
 23273   * are pointing to this hashmap, then release the whole instance.
 23274   */
 23275  JX9_PRIVATE void  jx9HashmapUnref(jx9_hashmap *pMap)
 23276  {
 23277  	pMap->iRef--;
 23278  	if( pMap->iRef < 1 ){
 23279  		jx9HashmapRelease(pMap, TRUE);
 23280  	}
 23281  }
 23282  /*
 23283   * Check if a given key exists in the given hashmap.
 23284   * Write a pointer to the target node on success.
 23285   * Otherwise SXERR_NOTFOUND is returned on failure.
 23286   */
 23287  JX9_PRIVATE sxi32 jx9HashmapLookup(
 23288  	jx9_hashmap *pMap,        /* Target hashmap */
 23289  	jx9_value *pKey,          /* Lookup key */
 23290  	jx9_hashmap_node **ppNode /* OUT: Target node on success */
 23291  	)
 23292  {
 23293  	sxi32 rc;
 23294  	if( pMap->nEntry < 1 ){
 23295  		/* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway.
 23296  		 */
 23297  		return SXERR_NOTFOUND;
 23298  	}
 23299  	rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
 23300  	return rc;
 23301  }
 23302  /*
 23303   * Insert a given key and it's associated value (if any) in the given
 23304   * hashmap.
 23305   * If a node with the given key already exists in the database
 23306   * then this function overwrite the old value.
 23307   */
 23308  JX9_PRIVATE sxi32 jx9HashmapInsert(
 23309  	jx9_hashmap *pMap, /* Target hashmap */
 23310  	jx9_value *pKey,   /* Lookup key */
 23311  	jx9_value *pVal    /* Node value.NULL otherwise */
 23312  	)
 23313  {
 23314  	sxi32 rc;
 23315  	rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
 23316  	return rc;
 23317  }
 23318  /*
 23319   * Reset the node cursor of a given hashmap.
 23320   */
 23321  JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap)
 23322  {
 23323  	/* Reset the loop cursor */
 23324  	pMap->pCur = pMap->pFirst;
 23325  }
 23326  /*
 23327   * Return a pointer to the node currently pointed by the node cursor.
 23328   * If the cursor reaches the end of the list, then this function
 23329   * return NULL.
 23330   * Note that the node cursor is automatically advanced by this function.
 23331   */
 23332  JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap)
 23333  {
 23334  	jx9_hashmap_node *pCur = pMap->pCur;
 23335  	if( pCur == 0 ){
 23336  		/* End of the list, return null */
 23337  		return 0;
 23338  	}
 23339  	/* Advance the node cursor */
 23340  	pMap->pCur = pCur->pPrev; /* Reverse link */
 23341  	return pCur;
 23342  }
 23343  /*
 23344   * Extract a node value.
 23345   */
 23346  JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode)
 23347  {
 23348  	jx9_value *pValue;
 23349  	pValue = HashmapExtractNodeValue(pNode);
 23350  	return pValue;
 23351  }
 23352  /*
 23353   * Extract a node value (Second).
 23354   */
 23355  JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore)
 23356  {
 23357  	jx9_value *pEntry = HashmapExtractNodeValue(pNode);
 23358  	if( pEntry ){
 23359  		if( bStore ){
 23360  			jx9MemObjStore(pEntry, pValue);
 23361  		}else{
 23362  			jx9MemObjLoad(pEntry, pValue);
 23363  		}
 23364  	}else{
 23365  		jx9MemObjRelease(pValue);
 23366  	}
 23367  }
 23368  /*
 23369   * Extract a node key.
 23370   */
 23371  JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey)
 23372  {
 23373  	/* Fill with the current key */
 23374  	if( pNode->iType == HASHMAP_INT_NODE ){
 23375  		if( SyBlobLength(&pKey->sBlob) > 0 ){
 23376  			SyBlobRelease(&pKey->sBlob);
 23377  		}
 23378  		pKey->x.iVal = pNode->xKey.iKey;
 23379  		MemObjSetType(pKey, MEMOBJ_INT);
 23380  	}else{
 23381  		SyBlobReset(&pKey->sBlob);
 23382  		SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
 23383  		MemObjSetType(pKey, MEMOBJ_STRING);
 23384  	}
 23385  }
 23386  #ifndef JX9_DISABLE_BUILTIN_FUNC
 23387  /*
 23388   * Store the address of nodes value in the given container.
 23389   * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations
 23390   * defined in 'builtin.c' for more information.
 23391   */
 23392  JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut)
 23393  {
 23394  	jx9_hashmap_node *pEntry = pMap->pFirst;
 23395  	jx9_value *pValue;
 23396  	sxu32 n;
 23397  	/* Initialize the container */
 23398  	SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *));
 23399  	for(n = 0 ; n < pMap->nEntry ; n++ ){
 23400  		/* Extract node value */
 23401  		pValue = HashmapExtractNodeValue(pEntry);
 23402  		if( pValue ){
 23403  			SySetPut(pOut, (const void *)&pValue);
 23404  		}
 23405  		/* Point to the next entry */
 23406  		pEntry = pEntry->pPrev; /* Reverse link */
 23407  	}
 23408  	/* Total inserted entries */
 23409  	return (int)SySetUsed(pOut);
 23410  }
 23411  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 23412  /*
 23413   * Merge sort.
 23414   * The merge sort implementation is based on the one found in the SQLite3 source tree.
 23415   * Status: Public domain
 23416   */
 23417  /* Node comparison callback signature */
 23418  typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *);
 23419  /*
 23420  ** Inputs:
 23421  **   a:       A sorted, null-terminated linked list.  (May be null).
 23422  **   b:       A sorted, null-terminated linked list.  (May be null).
 23423  **   cmp:     A pointer to the comparison function.
 23424  **
 23425  ** Return Value:
 23426  **   A pointer to the head of a sorted list containing the elements
 23427  **   of both a and b.
 23428  **
 23429  ** Side effects:
 23430  **   The "next", "prev" pointers for elements in the lists a and b are
 23431  **   changed.
 23432  */
 23433  static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData)
 23434  {
 23435  	jx9_hashmap_node result, *pTail;
 23436      /* Prevent compiler warning */
 23437  	result.pNext = result.pPrev = 0;
 23438  	pTail = &result;
 23439  	while( pA && pB ){
 23440  		if( xCmp(pA, pB, pCmpData) < 0 ){
 23441  			pTail->pPrev = pA;
 23442  			pA->pNext = pTail;
 23443  			pTail = pA;
 23444  			pA = pA->pPrev;
 23445  		}else{
 23446  			pTail->pPrev = pB;
 23447  			pB->pNext = pTail;
 23448  			pTail = pB;
 23449  			pB = pB->pPrev;
 23450  		}
 23451  	}
 23452  	if( pA ){
 23453  		pTail->pPrev = pA;
 23454  		pA->pNext = pTail;
 23455  	}else if( pB ){
 23456  		pTail->pPrev = pB;
 23457  		pB->pNext = pTail;
 23458  	}else{
 23459  		pTail->pPrev = pTail->pNext = 0;
 23460  	}
 23461  	return result.pPrev;
 23462  }
 23463  /*
 23464  ** Inputs:
 23465  **   Map:       Input hashmap
 23466  **   cmp:       A comparison function.
 23467  **
 23468  ** Return Value:
 23469  **   Sorted hashmap.
 23470  **
 23471  ** Side effects:
 23472  **   The "next" pointers for elements in list are changed.
 23473  */
 23474  #define N_SORT_BUCKET  32
 23475  static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData)
 23476  {
 23477  	jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
 23478  	sxu32 i;
 23479  	SyZero(a, sizeof(a));
 23480  	/* Point to the first inserted entry */
 23481  	pIn = pMap->pFirst;
 23482  	while( pIn ){
 23483  		p = pIn;
 23484  		pIn = p->pPrev;
 23485  		p->pPrev = 0;
 23486  		for(i=0; i<N_SORT_BUCKET-1; i++){
 23487  			if( a[i]==0 ){
 23488  				a[i] = p;
 23489  				break;
 23490  			}else{
 23491  				p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
 23492  				a[i] = 0;
 23493  			}
 23494  		}
 23495  		if( i==N_SORT_BUCKET-1 ){
 23496  			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
 23497  			 * But that is impossible.
 23498  			 */
 23499  			a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
 23500  		}
 23501  	}
 23502  	p = a[0];
 23503  	for(i=1; i<N_SORT_BUCKET; i++){
 23504  		p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
 23505  	}
 23506  	p->pNext = 0;
 23507  	/* Reflect the change */
 23508  	pMap->pFirst = p;
 23509  	/* Reset the loop cursor */
 23510  	pMap->pCur = pMap->pFirst;
 23511  	return SXRET_OK;
 23512  }
 23513  /* 
 23514   * Node comparison callback.
 23515   * used-by: [sort(), asort(), ...]
 23516   */
 23517  static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
 23518  {
 23519  	jx9_value sA, sB;
 23520  	sxi32 iFlags;
 23521  	int rc;
 23522  	if( pCmpData == 0 ){
 23523  		/* Perform a standard comparison */
 23524  		rc = HashmapNodeCmp(pA, pB, FALSE);
 23525  		return rc;
 23526  	}
 23527  	iFlags = SX_PTR_TO_INT(pCmpData);
 23528  	/* Duplicate node values */
 23529  	jx9MemObjInit(pA->pMap->pVm, &sA);
 23530  	jx9MemObjInit(pA->pMap->pVm, &sB);
 23531  	jx9HashmapExtractNodeValue(pA, &sA, FALSE);
 23532  	jx9HashmapExtractNodeValue(pB, &sB, FALSE);
 23533  	if( iFlags == 5 ){
 23534  		/* String cast */
 23535  		if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
 23536  			jx9MemObjToString(&sA);
 23537  		}
 23538  		if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
 23539  			jx9MemObjToString(&sB);
 23540  		}
 23541  	}else{
 23542  		/* Numeric cast */
 23543  		jx9MemObjToNumeric(&sA);
 23544  		jx9MemObjToNumeric(&sB);
 23545  	}
 23546  	/* Perform the comparison */
 23547  	rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
 23548  	jx9MemObjRelease(&sA);
 23549  	jx9MemObjRelease(&sB);
 23550  	return rc;
 23551  }
 23552  /*
 23553   * Node comparison callback.
 23554   * Used by: [rsort(), arsort()];
 23555   */
 23556  static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
 23557  {
 23558  	jx9_value sA, sB;
 23559  	sxi32 iFlags;
 23560  	int rc;
 23561  	if( pCmpData == 0 ){
 23562  		/* Perform a standard comparison */
 23563  		rc = HashmapNodeCmp(pA, pB, FALSE);
 23564  		return -rc;
 23565  	}
 23566  	iFlags = SX_PTR_TO_INT(pCmpData);
 23567  	/* Duplicate node values */
 23568  	jx9MemObjInit(pA->pMap->pVm, &sA);
 23569  	jx9MemObjInit(pA->pMap->pVm, &sB);
 23570  	jx9HashmapExtractNodeValue(pA, &sA, FALSE);
 23571  	jx9HashmapExtractNodeValue(pB, &sB, FALSE);
 23572  	if( iFlags == 5 ){
 23573  		/* String cast */
 23574  		if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
 23575  			jx9MemObjToString(&sA);
 23576  		}
 23577  		if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
 23578  			jx9MemObjToString(&sB);
 23579  		}
 23580  	}else{
 23581  		/* Numeric cast */
 23582  		jx9MemObjToNumeric(&sA);
 23583  		jx9MemObjToNumeric(&sB);
 23584  	}
 23585  	/* Perform the comparison */
 23586  	rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
 23587  	jx9MemObjRelease(&sA);
 23588  	jx9MemObjRelease(&sB);
 23589  	return -rc;
 23590  }
 23591  /*
 23592   * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
 23593   * used-by: [usort(), uasort()]
 23594   */
 23595  static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
 23596  {
 23597  	jx9_value sResult, *pCallback;
 23598  	jx9_value *pV1, *pV2;
 23599  	jx9_value *apArg[2];  /* Callback arguments */
 23600  	sxi32 rc;
 23601  	/* Point to the desired callback */
 23602  	pCallback = (jx9_value *)pCmpData;
 23603  	/* initialize the result value */
 23604  	jx9MemObjInit(pA->pMap->pVm, &sResult);
 23605  	/* Extract nodes values */
 23606  	pV1 = HashmapExtractNodeValue(pA);
 23607  	pV2 = HashmapExtractNodeValue(pB);
 23608  	apArg[0] = pV1;
 23609  	apArg[1] = pV2;
 23610  	/* Invoke the callback */
 23611  	rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
 23612  	if( rc != SXRET_OK ){
 23613  		/* An error occured while calling user defined function [i.e: not defined] */
 23614  		rc = -1; /* Set a dummy result */
 23615  	}else{
 23616  		/* Extract callback result */
 23617  		if((sResult.iFlags & MEMOBJ_INT) == 0 ){
 23618  			/* Perform an int cast */
 23619  			jx9MemObjToInteger(&sResult);
 23620  		}
 23621  		rc = (sxi32)sResult.x.iVal;
 23622  	}
 23623  	jx9MemObjRelease(&sResult);
 23624  	/* Callback result */
 23625  	return rc;
 23626  }
 23627  /*
 23628   * Rehash all nodes keys after a merge-sort have been applied.
 23629   * Used by [sort(), usort() and rsort()].
 23630   */
 23631  static void HashmapSortRehash(jx9_hashmap *pMap)
 23632  {
 23633  	jx9_hashmap_node *p, *pLast;
 23634  	sxu32 i;
 23635  	/* Rehash all entries */
 23636  	pLast = p = pMap->pFirst;
 23637  	pMap->iNextIdx = 0; /* Reset the automatic index */
 23638  	i = 0;
 23639  	for( ;; ){
 23640  		if( i >= pMap->nEntry ){
 23641  			pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
 23642  			break;
 23643  		}
 23644  		if( p->iType == HASHMAP_BLOB_NODE ){
 23645  			/* Do not maintain index association as requested by the JX9 specification */
 23646  			SyBlobRelease(&p->xKey.sKey);
 23647  			/* Change key type */
 23648  			p->iType = HASHMAP_INT_NODE;
 23649  		}
 23650  		HashmapRehashIntNode(p);
 23651  		/* Point to the next entry */
 23652  		i++;
 23653  		pLast = p;
 23654  		p = p->pPrev; /* Reverse link */
 23655  	}
 23656  }
 23657  /*
 23658   * Array functions implementation.
 23659   * Authors:
 23660   *  Symisc Systems, devel@symisc.net.
 23661   *  Copyright (C) Symisc Systems, http://jx9.symisc.net
 23662   * Status:
 23663   *  Stable.
 23664   */
 23665  /*
 23666   * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] )
 23667   * Sort an array.
 23668   * Parameters
 23669   *  $array
 23670   *   The input array.
 23671   * $sort_flags
 23672   *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 23673   *  Sorting type flags:
 23674   *   SORT_REGULAR - compare items normally (don't change types)
 23675   *   SORT_NUMERIC - compare items numerically
 23676   *   SORT_STRING - compare items as strings
 23677   * Return
 23678   *  TRUE on success or FALSE on failure.
 23679   * 
 23680   */
 23681  static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23682  {
 23683  	jx9_hashmap *pMap;
 23684  	/* Make sure we are dealing with a valid hashmap */
 23685  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 23686  		/* Missing/Invalid arguments, return FALSE */
 23687  		jx9_result_bool(pCtx, 0);
 23688  		return JX9_OK;
 23689  	}
 23690  	/* Point to the internal representation of the input hashmap */
 23691  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23692  	if( pMap->nEntry > 1 ){
 23693  		sxi32 iCmpFlags = 0;
 23694  		if( nArg > 1 ){
 23695  			/* Extract comparison flags */
 23696  			iCmpFlags = jx9_value_to_int(apArg[1]);
 23697  			if( iCmpFlags == 3 /* SORT_REGULAR */ ){
 23698  				iCmpFlags = 0; /* Standard comparison */
 23699  			}
 23700  		}
 23701  		/* Do the merge sort */
 23702  		HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
 23703  		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
 23704  		HashmapSortRehash(pMap);
 23705  	}
 23706  	/* All done, return TRUE */
 23707  	jx9_result_bool(pCtx, 1);
 23708  	return JX9_OK;
 23709  }
 23710  /*
 23711   * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] )
 23712   * Sort an array in reverse order.
 23713   * Parameters
 23714   *  $array
 23715   *   The input array.
 23716   * $sort_flags
 23717   *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 23718   *  Sorting type flags:
 23719   *   SORT_REGULAR - compare items normally (don't change types)
 23720   *   SORT_NUMERIC - compare items numerically
 23721   *   SORT_STRING - compare items as strings
 23722   * Return
 23723   *  TRUE on success or FALSE on failure.
 23724   */
 23725  static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23726  {
 23727  	jx9_hashmap *pMap;
 23728  	/* Make sure we are dealing with a valid hashmap */
 23729  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 23730  		/* Missing/Invalid arguments, return FALSE */
 23731  		jx9_result_bool(pCtx, 0);
 23732  		return JX9_OK;
 23733  	}
 23734  	/* Point to the internal representation of the input hashmap */
 23735  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23736  	if( pMap->nEntry > 1 ){
 23737  		sxi32 iCmpFlags = 0;
 23738  		if( nArg > 1 ){
 23739  			/* Extract comparison flags */
 23740  			iCmpFlags = jx9_value_to_int(apArg[1]);
 23741  			if( iCmpFlags == 3 /* SORT_REGULAR */ ){
 23742  				iCmpFlags = 0; /* Standard comparison */
 23743  			}
 23744  		}
 23745  		/* Do the merge sort */
 23746  		HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
 23747  		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
 23748  		HashmapSortRehash(pMap);
 23749  	}
 23750  	/* All done, return TRUE */
 23751  	jx9_result_bool(pCtx, 1);
 23752  	return JX9_OK;
 23753  }
 23754  /*
 23755   * bool usort(array &$array, callable $cmp_function)
 23756   *  Sort an array by values using a user-defined comparison function.
 23757   * Parameters
 23758   *  $array
 23759   *   The input array.
 23760   * $cmp_function
 23761   *  The comparison function must return an integer less than, equal to, or greater
 23762   *  than zero if the first argument is considered to be respectively less than, equal
 23763   *  to, or greater than the second.
 23764   *    int callback ( mixed $a, mixed $b )
 23765   * Return
 23766   *  TRUE on success or FALSE on failure.
 23767   */
 23768  static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23769  {
 23770  	jx9_hashmap *pMap;
 23771  	/* Make sure we are dealing with a valid hashmap */
 23772  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 23773  		/* Missing/Invalid arguments, return FALSE */
 23774  		jx9_result_bool(pCtx, 0);
 23775  		return JX9_OK;
 23776  	}
 23777  	/* Point to the internal representation of the input hashmap */
 23778  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23779  	if( pMap->nEntry > 1 ){
 23780  		jx9_value *pCallback = 0;
 23781  		ProcNodeCmp xCmp;
 23782  		xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
 23783  		if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){
 23784  			/* Point to the desired callback */
 23785  			pCallback = apArg[1];
 23786  		}else{
 23787  			/* Use the default comparison function */
 23788  			xCmp = HashmapCmpCallback1;
 23789  		}
 23790  		/* Do the merge sort */
 23791  		HashmapMergeSort(pMap, xCmp, pCallback);
 23792  		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
 23793  		HashmapSortRehash(pMap);
 23794  	}
 23795  	/* All done, return TRUE */
 23796  	jx9_result_bool(pCtx, 1);
 23797  	return JX9_OK;
 23798  }
 23799  /*
 23800   * int count(array $var [, int $mode = COUNT_NORMAL ])
 23801   *   Count all elements in an array, or something in an object.
 23802   * Parameters
 23803   *  $var
 23804   *   The array or the object.
 23805   * $mode
 23806   *  If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
 23807   *  will recursively count the array. This is particularly useful for counting 
 23808   *  all the elements of a multidimensional array. count() does not detect infinite
 23809   *  recursion.
 23810   * Return
 23811   *  Returns the number of elements in the array.
 23812   */
 23813  static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23814  {
 23815  	int bRecursive = FALSE;
 23816  	sxi64 iCount;
 23817  	if( nArg < 1 ){
 23818  		/* Missing arguments, return 0 */
 23819  		jx9_result_int(pCtx, 0);
 23820  		return JX9_OK;
 23821  	}
 23822  	if( !jx9_value_is_json_array(apArg[0]) ){
 23823  		/* TICKET 1433-19: Handle objects */
 23824  		int res = !jx9_value_is_null(apArg[0]);
 23825  		jx9_result_int(pCtx, res);
 23826  		return JX9_OK;
 23827  	}
 23828  	if( nArg > 1 ){
 23829  		/* Recursive count? */
 23830  		bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
 23831  	}
 23832  	/* Count */
 23833  	iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
 23834  	jx9_result_int64(pCtx, iCount);
 23835  	return JX9_OK;
 23836  }
 23837  /*
 23838   * bool array_key_exists(value $key, array $search)
 23839   *  Checks if the given key or index exists in the array.
 23840   * Parameters
 23841   * $key
 23842   *   Value to check.
 23843   * $search
 23844   *  An array with keys to check.
 23845   * Return
 23846   *  TRUE on success or FALSE on failure.
 23847   */
 23848  static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23849  {
 23850  	sxi32 rc;
 23851  	if( nArg < 2 ){
 23852  		/* Missing arguments, return FALSE */
 23853  		jx9_result_bool(pCtx, 0);
 23854  		return JX9_OK;
 23855  	}
 23856  	/* Make sure we are dealing with a valid hashmap */
 23857  	if( !jx9_value_is_json_array(apArg[1]) ){
 23858  		/* Invalid argument, return FALSE */
 23859  		jx9_result_bool(pCtx, 0);
 23860  		return JX9_OK;
 23861  	}
 23862  	/* Perform the lookup */
 23863  	rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
 23864  	/* lookup result */
 23865  	jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
 23866  	return JX9_OK;
 23867  }
 23868  /*
 23869   * value array_pop(array $array)
 23870   *   POP the last inserted element from the array.
 23871   * Parameter
 23872   *  The array to get the value from.
 23873   * Return
 23874   *  Poped value or NULL on failure.
 23875   */
 23876  static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23877  {
 23878  	jx9_hashmap *pMap;
 23879  	if( nArg < 1 ){
 23880  		/* Missing arguments, return null */
 23881  		jx9_result_null(pCtx);
 23882  		return JX9_OK;
 23883  	}
 23884  	/* Make sure we are dealing with a valid hashmap */
 23885  	if( !jx9_value_is_json_array(apArg[0]) ){
 23886  		/* Invalid argument, return null */
 23887  		jx9_result_null(pCtx);
 23888  		return JX9_OK;
 23889  	}
 23890  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23891  	if( pMap->nEntry < 1 ){
 23892  		/* Noting to pop, return NULL */
 23893  		jx9_result_null(pCtx);
 23894  	}else{
 23895  		jx9_hashmap_node *pLast = pMap->pLast;
 23896  		jx9_value *pObj;
 23897  		pObj = HashmapExtractNodeValue(pLast);
 23898  		if( pObj ){
 23899  			/* Node value */
 23900  			jx9_result_value(pCtx, pObj);
 23901  			/* Unlink the node */
 23902  			jx9HashmapUnlinkNode(pLast);
 23903  		}else{
 23904  			jx9_result_null(pCtx);
 23905  		}
 23906  		/* Reset the cursor */
 23907  		pMap->pCur = pMap->pFirst;
 23908  	}
 23909  	return JX9_OK;
 23910  }
 23911  /*
 23912   * int array_push($array, $var, ...)
 23913   *   Push one or more elements onto the end of array. (Stack insertion)
 23914   * Parameters
 23915   *  array
 23916   *    The input array.
 23917   *  var
 23918   *   On or more value to push.
 23919   * Return
 23920   *  New array count (including old items).
 23921   */
 23922  static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23923  {
 23924  	jx9_hashmap *pMap;
 23925  	sxi32 rc;
 23926  	int i;
 23927  	if( nArg < 1 ){
 23928  		/* Missing arguments, return 0 */
 23929  		jx9_result_int(pCtx, 0);
 23930  		return JX9_OK;
 23931  	}
 23932  	/* Make sure we are dealing with a valid hashmap */
 23933  	if( !jx9_value_is_json_array(apArg[0]) ){
 23934  		/* Invalid argument, return 0 */
 23935  		jx9_result_int(pCtx, 0);
 23936  		return JX9_OK;
 23937  	}
 23938  	/* Point to the internal representation of the input hashmap */
 23939  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23940  	/* Start pushing given values */
 23941  	for( i = 1 ; i < nArg ; ++i ){
 23942  		rc = jx9HashmapInsert(pMap, 0, apArg[i]);
 23943  		if( rc != SXRET_OK ){
 23944  			break;
 23945  		}
 23946  	}
 23947  	/* Return the new count */
 23948  	jx9_result_int64(pCtx, (sxi64)pMap->nEntry);
 23949  	return JX9_OK;
 23950  }
 23951  /*
 23952   * value array_shift(array $array)
 23953   *   Shift an element off the beginning of array.
 23954   * Parameter
 23955   *  The array to get the value from.
 23956   * Return
 23957   *  Shifted value or NULL on failure.
 23958   */
 23959  static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg)
 23960  {
 23961  	jx9_hashmap *pMap;
 23962  	if( nArg < 1 ){
 23963  		/* Missing arguments, return null */
 23964  		jx9_result_null(pCtx);
 23965  		return JX9_OK;
 23966  	}
 23967  	/* Make sure we are dealing with a valid hashmap */
 23968  	if( !jx9_value_is_json_array(apArg[0]) ){
 23969  		/* Invalid argument, return null */
 23970  		jx9_result_null(pCtx);
 23971  		return JX9_OK;
 23972  	}
 23973  	/* Point to the internal representation of the hashmap */
 23974  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 23975  	if( pMap->nEntry < 1 ){
 23976  		/* Empty hashmap, return NULL */
 23977  		jx9_result_null(pCtx);
 23978  	}else{
 23979  		jx9_hashmap_node *pEntry = pMap->pFirst;
 23980  		jx9_value *pObj;
 23981  		sxu32 n;
 23982  		pObj = HashmapExtractNodeValue(pEntry);
 23983  		if( pObj ){
 23984  			/* Node value */
 23985  			jx9_result_value(pCtx, pObj);
 23986  			/* Unlink the first node */
 23987  			jx9HashmapUnlinkNode(pEntry);
 23988  		}else{
 23989  			jx9_result_null(pCtx);
 23990  		}
 23991  		/* Rehash all int keys */
 23992  		n = pMap->nEntry;
 23993  		pEntry = pMap->pFirst;
 23994  		pMap->iNextIdx = 0; /* Reset the automatic index */
 23995  		for(;;){
 23996  			if( n < 1 ){
 23997  				break;
 23998  			}
 23999  			if( pEntry->iType == HASHMAP_INT_NODE ){
 24000  				HashmapRehashIntNode(pEntry);
 24001  			}
 24002  			/* Point to the next entry */
 24003  			pEntry = pEntry->pPrev; /* Reverse link */
 24004  			n--;
 24005  		}
 24006  		/* Reset the cursor */
 24007  		pMap->pCur = pMap->pFirst;
 24008  	}
 24009  	return JX9_OK;
 24010  }
 24011  /*
 24012   * Extract the node cursor value.
 24013   */
 24014  static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection)
 24015  {
 24016  	jx9_hashmap_node *pCur = pMap->pCur;
 24017  	jx9_value *pVal;
 24018  	if( pCur == 0 ){
 24019  		/* Cursor does not point to anything, return FALSE */
 24020  		jx9_result_bool(pCtx, 0);
 24021  		return JX9_OK;
 24022  	}
 24023  	if( iDirection != 0 ){
 24024  		if( iDirection > 0 ){
 24025  			/* Point to the next entry */
 24026  			pMap->pCur = pCur->pPrev; /* Reverse link */
 24027  			pCur = pMap->pCur;
 24028  		}else{
 24029  			/* Point to the previous entry */
 24030  			pMap->pCur = pCur->pNext; /* Reverse link */
 24031  			pCur = pMap->pCur;
 24032  		}
 24033  		if( pCur == 0 ){
 24034  			/* End of input reached, return FALSE */
 24035  			jx9_result_bool(pCtx, 0);
 24036  			return JX9_OK;
 24037  		}
 24038  	}		
 24039  	/* Point to the desired element */
 24040  	pVal = HashmapExtractNodeValue(pCur);
 24041  	if( pVal ){
 24042  		jx9_result_value(pCtx, pVal);
 24043  	}else{
 24044  		jx9_result_bool(pCtx, 0);
 24045  	}
 24046  	return JX9_OK;
 24047  }
 24048  /*
 24049   * value current(array $array)
 24050   *  Return the current element in an array.
 24051   * Parameter
 24052   *  $input: The input array.
 24053   * Return
 24054   *  The current() function simply returns the value of the array element that's currently
 24055   *  being pointed to by the internal pointer. It does not move the pointer in any way.
 24056   *  If the internal pointer points beyond the end of the elements list or the array 
 24057   *  is empty, current() returns FALSE. 
 24058   */
 24059  static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24060  {
 24061  	if( nArg < 1 ){
 24062  		/* Missing arguments, return FALSE */
 24063  		jx9_result_bool(pCtx, 0);
 24064  		return JX9_OK;
 24065  	}
 24066  	/* Make sure we are dealing with a valid hashmap */
 24067  	if( !jx9_value_is_json_array(apArg[0]) ){
 24068  		/* Invalid argument, return FALSE */
 24069  		jx9_result_bool(pCtx, 0);
 24070  		return JX9_OK;
 24071  	}
 24072  	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0);
 24073  	return JX9_OK;
 24074  }
 24075  /*
 24076   * value next(array $input)
 24077   *  Advance the internal array pointer of an array.
 24078   * Parameter
 24079   *  $input: The input array.
 24080   * Return
 24081   *  next() behaves like current(), with one difference. It advances the internal array 
 24082   *  pointer one place forward before returning the element value. That means it returns 
 24083   *  the next array value and advances the internal array pointer by one. 
 24084   */
 24085  static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24086  {
 24087  	if( nArg < 1 ){
 24088  		/* Missing arguments, return FALSE */
 24089  		jx9_result_bool(pCtx, 0);
 24090  		return JX9_OK;
 24091  	}
 24092  	/* Make sure we are dealing with a valid hashmap */
 24093  	if( !jx9_value_is_json_array(apArg[0]) ){
 24094  		/* Invalid argument, return FALSE */
 24095  		jx9_result_bool(pCtx, 0);
 24096  		return JX9_OK;
 24097  	}
 24098  	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1);
 24099  	return JX9_OK;
 24100  }
 24101  /* 
 24102   * value prev(array $input)
 24103   *  Rewind the internal array pointer.
 24104   * Parameter
 24105   *  $input: The input array.
 24106   * Return
 24107   *  Returns the array value in the previous place that's pointed
 24108   *  to by the internal array pointer, or FALSE if there are no more
 24109   *  elements. 
 24110   */
 24111  static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24112  {
 24113  	if( nArg < 1 ){
 24114  		/* Missing arguments, return FALSE */
 24115  		jx9_result_bool(pCtx, 0);
 24116  		return JX9_OK;
 24117  	}
 24118  	/* Make sure we are dealing with a valid hashmap */
 24119  	if( !jx9_value_is_json_array(apArg[0]) ){
 24120  		/* Invalid argument, return FALSE */
 24121  		jx9_result_bool(pCtx, 0);
 24122  		return JX9_OK;
 24123  	}
 24124  	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1);
 24125  	return JX9_OK;
 24126  }
 24127  /* 
 24128   * value end(array $input)
 24129   *  Set the internal pointer of an array to its last element.
 24130   * Parameter
 24131   *  $input: The input array.
 24132   * Return
 24133   *  Returns the value of the last element or FALSE for empty array. 
 24134   */
 24135  static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24136  {
 24137  	jx9_hashmap *pMap;
 24138  	if( nArg < 1 ){
 24139  		/* Missing arguments, return FALSE */
 24140  		jx9_result_bool(pCtx, 0);
 24141  		return JX9_OK;
 24142  	}
 24143  	/* Make sure we are dealing with a valid hashmap */
 24144  	if( !jx9_value_is_json_array(apArg[0]) ){
 24145  		/* Invalid argument, return FALSE */
 24146  		jx9_result_bool(pCtx, 0);
 24147  		return JX9_OK;
 24148  	}
 24149  	/* Point to the internal representation of the input hashmap */
 24150  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24151  	/* Point to the last node */
 24152  	pMap->pCur = pMap->pLast;
 24153  	/* Return the last node value */
 24154  	HashmapCurrentValue(&(*pCtx), pMap, 0);
 24155  	return JX9_OK;
 24156  }
 24157  /* 
 24158   * value reset(array $array )
 24159   *  Set the internal pointer of an array to its first element.
 24160   * Parameter
 24161   *  $input: The input array.
 24162   * Return
 24163   *  Returns the value of the first array element, or FALSE if the array is empty. 
 24164   */
 24165  static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24166  {
 24167  	jx9_hashmap *pMap;
 24168  	if( nArg < 1 ){
 24169  		/* Missing arguments, return FALSE */
 24170  		jx9_result_bool(pCtx, 0);
 24171  		return JX9_OK;
 24172  	}
 24173  	/* Make sure we are dealing with a valid hashmap */
 24174  	if( !jx9_value_is_json_array(apArg[0]) ){
 24175  		/* Invalid argument, return FALSE */
 24176  		jx9_result_bool(pCtx, 0);
 24177  		return JX9_OK;
 24178  	}
 24179  	/* Point to the internal representation of the input hashmap */
 24180  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24181  	/* Point to the first node */
 24182  	pMap->pCur = pMap->pFirst;
 24183  	/* Return the last node value if available */
 24184  	HashmapCurrentValue(&(*pCtx), pMap, 0);
 24185  	return JX9_OK;
 24186  }
 24187  /*
 24188   * value key(array $array)
 24189   *   Fetch a key from an array
 24190   * Parameter
 24191   *  $input
 24192   *   The input array.
 24193   * Return
 24194   *  The key() function simply returns the key of the array element that's currently
 24195   *  being pointed to by the internal pointer. It does not move the pointer in any way.
 24196   *  If the internal pointer points beyond the end of the elements list or the array 
 24197   *  is empty, key() returns NULL. 
 24198   */
 24199  static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24200  {
 24201  	jx9_hashmap_node *pCur;
 24202  	jx9_hashmap *pMap;
 24203  	if( nArg < 1 ){
 24204  		/* Missing arguments, return NULL */
 24205  		jx9_result_null(pCtx);
 24206  		return JX9_OK;
 24207  	}
 24208  	/* Make sure we are dealing with a valid hashmap */
 24209  	if( !jx9_value_is_json_array(apArg[0]) ){
 24210  		/* Invalid argument, return NULL */
 24211  		jx9_result_null(pCtx);
 24212  		return JX9_OK;
 24213  	}
 24214  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24215  	pCur = pMap->pCur;
 24216  	if( pCur == 0 ){
 24217  		/* Cursor does not point to anything, return NULL */
 24218  		jx9_result_null(pCtx);
 24219  		return JX9_OK;
 24220  	}
 24221  	if( pCur->iType == HASHMAP_INT_NODE){
 24222  		/* Key is integer */
 24223  		jx9_result_int64(pCtx, pCur->xKey.iKey);
 24224  	}else{
 24225  		/* Key is blob */
 24226  		jx9_result_string(pCtx, 
 24227  			(const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
 24228  	}
 24229  	return JX9_OK;
 24230  }
 24231  /*
 24232   * array each(array $input)
 24233   *  Return the current key and value pair from an array and advance the array cursor.
 24234   * Parameter
 24235   *  $input
 24236   *    The input array.
 24237   * Return
 24238   *  Returns the current key and value pair from the array array. This pair is returned 
 24239   *  in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key 
 24240   *  contain the key name of the array element, and 1 and value contain the data.
 24241   *  If the internal pointer for the array points past the end of the array contents
 24242   *  each() returns FALSE. 
 24243   */
 24244  static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24245  {
 24246  	jx9_hashmap_node *pCur;
 24247  	jx9_hashmap *pMap;
 24248  	jx9_value *pArray;
 24249  	jx9_value *pVal;
 24250  	jx9_value sKey;
 24251  	if( nArg < 1 ){
 24252  		/* Missing arguments, return FALSE */
 24253  		jx9_result_bool(pCtx, 0);
 24254  		return JX9_OK;
 24255  	}
 24256  	/* Make sure we are dealing with a valid hashmap */
 24257  	if( !jx9_value_is_json_array(apArg[0]) ){
 24258  		/* Invalid argument, return FALSE */
 24259  		jx9_result_bool(pCtx, 0);
 24260  		return JX9_OK;
 24261  	}
 24262  	/* Point to the internal representation that describe the input hashmap */
 24263  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24264  	if( pMap->pCur == 0 ){
 24265  		/* Cursor does not point to anything, return FALSE */
 24266  		jx9_result_bool(pCtx, 0);
 24267  		return JX9_OK;
 24268  	}
 24269  	pCur = pMap->pCur;
 24270  	/* Create a new array */
 24271  	pArray = jx9_context_new_array(pCtx);
 24272  	if( pArray == 0 ){
 24273  		jx9_result_bool(pCtx, 0);
 24274  		return JX9_OK;
 24275  	}
 24276  	pVal = HashmapExtractNodeValue(pCur);
 24277  	/* Insert the current value */
 24278  	jx9_array_add_strkey_elem(pArray, "1", pVal);
 24279  	jx9_array_add_strkey_elem(pArray, "value", pVal);
 24280  	/* Make the key */
 24281  	if( pCur->iType == HASHMAP_INT_NODE ){
 24282  		jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
 24283  	}else{
 24284  		jx9MemObjInitFromString(pMap->pVm, &sKey, 0);
 24285  		jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
 24286  	}
 24287  	/* Insert the current key */
 24288  	jx9_array_add_elem(pArray, 0, &sKey);
 24289  	jx9_array_add_strkey_elem(pArray, "key", &sKey);
 24290  	jx9MemObjRelease(&sKey);
 24291  	/* Advance the cursor */
 24292  	pMap->pCur = pCur->pPrev; /* Reverse link */
 24293  	/* Return the current entry */
 24294  	jx9_result_value(pCtx, pArray);
 24295  	return JX9_OK;
 24296  }
 24297  /*
 24298   * array array_values(array $input)
 24299   *   Returns all the values from the input array and indexes numerically the array.
 24300   * Parameters
 24301   *   input: The input array.
 24302   * Return
 24303   *  An indexed array of values or NULL on failure.
 24304   */
 24305  static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24306  {
 24307  	jx9_hashmap_node *pNode;
 24308  	jx9_hashmap *pMap;
 24309  	jx9_value *pArray;
 24310  	jx9_value *pObj;
 24311  	sxu32 n;
 24312  	if( nArg < 1 ){
 24313  		/* Missing arguments, return NULL */
 24314  		jx9_result_null(pCtx);
 24315  		return JX9_OK;
 24316  	}
 24317  	/* Make sure we are dealing with a valid hashmap */
 24318  	if( !jx9_value_is_json_array(apArg[0]) ){
 24319  		/* Invalid argument, return NULL */
 24320  		jx9_result_null(pCtx);
 24321  		return JX9_OK;
 24322  	}
 24323  	/* Point to the internal representation that describe the input hashmap */
 24324  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24325  	/* Create a new array */
 24326  	pArray = jx9_context_new_array(pCtx);
 24327  	if( pArray == 0 ){
 24328  		jx9_result_null(pCtx);
 24329  		return JX9_OK;
 24330  	}
 24331  	/* Perform the requested operation */
 24332  	pNode = pMap->pFirst;
 24333  	for( n = 0 ; n < pMap->nEntry ; ++n ){
 24334  		pObj = HashmapExtractNodeValue(pNode);
 24335  		if( pObj ){
 24336  			/* perform the insertion */
 24337  			jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
 24338  		}
 24339  		/* Point to the next entry */
 24340  		pNode = pNode->pPrev; /* Reverse link */
 24341  	}
 24342  	/* return the new array */
 24343  	jx9_result_value(pCtx, pArray);
 24344  	return JX9_OK;
 24345  }
 24346  /*
 24347   * bool array_same(array $arr1, array $arr2)
 24348   *  Return TRUE if the given arrays are the same instance.
 24349   *  This function is useful under JX9 since arrays and objects
 24350   *  are passed by reference.
 24351   * Parameters
 24352   *  $arr1
 24353   *   First array
 24354   *  $arr2
 24355   *   Second array
 24356   * Return
 24357   *  TRUE if the arrays are the same instance. FALSE otherwise.
 24358   * Note
 24359   *  This function is a symisc eXtension.
 24360   */
 24361  static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24362  {
 24363  	jx9_hashmap *p1, *p2;
 24364  	int rc;
 24365  	if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
 24366  		/* Missing or invalid arguments, return FALSE*/
 24367  		jx9_result_bool(pCtx, 0);
 24368  		return JX9_OK;
 24369  	}
 24370  	/* Point to the hashmaps */
 24371  	p1 = (jx9_hashmap *)apArg[0]->x.pOther;
 24372  	p2 = (jx9_hashmap *)apArg[1]->x.pOther;
 24373  	rc = (p1 == p2);
 24374  	/* Same instance? */
 24375  	jx9_result_bool(pCtx, rc);
 24376  	return JX9_OK;
 24377  }
 24378  /*
 24379   * array array_merge(array $array1, ...)
 24380   *  Merge one or more arrays.
 24381   * Parameters
 24382   *  $array1
 24383   *    Initial array to merge.
 24384   *  ...
 24385   *   More array to merge.
 24386   * Return
 24387   *  The resulting array.
 24388   */
 24389  static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24390  {
 24391  	jx9_hashmap *pMap, *pSrc;
 24392  	jx9_value *pArray;
 24393  	int i;
 24394  	if( nArg < 1 ){
 24395  		/* Missing arguments, return NULL */
 24396  		jx9_result_null(pCtx);
 24397  		return JX9_OK;
 24398  	}
 24399  	/* Create a new array */
 24400  	pArray = jx9_context_new_array(pCtx);
 24401  	if( pArray == 0 ){
 24402  		jx9_result_null(pCtx);
 24403  		return JX9_OK;
 24404  	}
 24405  	/* Point to the internal representation of the hashmap */
 24406  	pMap = (jx9_hashmap *)pArray->x.pOther;
 24407  	/* Start merging */
 24408  	for( i = 0 ; i < nArg ; i++ ){
 24409  		/* Make sure we are dealing with a valid hashmap */
 24410  		if( !jx9_value_is_json_array(apArg[i]) ){
 24411  			/* Insert scalar value */
 24412  			jx9_array_add_elem(pArray, 0, apArg[i]);
 24413  		}else{
 24414  			pSrc = (jx9_hashmap *)apArg[i]->x.pOther;
 24415  			/* Merge the two hashmaps */
 24416  			HashmapMerge(pSrc, pMap);
 24417  		}
 24418  	}
 24419  	/* Return the freshly created array */
 24420  	jx9_result_value(pCtx, pArray);
 24421  	return JX9_OK;
 24422  }
 24423  /*
 24424   * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ])
 24425   *  Checks if a value exists in an array.
 24426   * Parameters
 24427   *  $needle
 24428   *   The searched value.
 24429   *   Note:
 24430   *    If needle is a string, the comparison is done in a case-sensitive manner.
 24431   * $haystack
 24432   *  The target array.
 24433   * $strict
 24434   *  If the third parameter strict is set to TRUE then the in_array() function
 24435   *  will also check the types of the needle in the haystack.
 24436   */
 24437  static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24438  {
 24439  	jx9_value *pNeedle;
 24440  	int bStrict;
 24441  	int rc;
 24442  	if( nArg < 2 ){
 24443  		/* Missing argument, return FALSE */
 24444  		jx9_result_bool(pCtx, 0);
 24445  		return JX9_OK;
 24446  	}
 24447  	pNeedle = apArg[0];
 24448  	bStrict = 0;
 24449  	if( nArg > 2 ){
 24450  		bStrict = jx9_value_to_bool(apArg[2]);
 24451  	}
 24452  	if( !jx9_value_is_json_array(apArg[1]) ){
 24453  		/* haystack must be an array, perform a standard comparison */ 
 24454  		rc = jx9_value_compare(pNeedle, apArg[1], bStrict);
 24455  		/* Set the comparison result */
 24456  		jx9_result_bool(pCtx, rc == 0);
 24457  		return JX9_OK;
 24458  	}
 24459  	/* Perform the lookup */
 24460  	rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
 24461  	/* Lookup result */
 24462  	jx9_result_bool(pCtx, rc == SXRET_OK);
 24463  	return JX9_OK;
 24464  }
 24465  /*
 24466   * array array_copy(array $source)
 24467   *  Make a blind copy of the target array.
 24468   * Parameters
 24469   *  $source
 24470   *   Target array
 24471   * Return
 24472   *  Copy of the target array on success. NULL otherwise.
 24473   * Note
 24474   *  This function is a symisc eXtension.
 24475   */
 24476  static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24477  {
 24478  	jx9_hashmap *pMap;
 24479  	jx9_value *pArray;
 24480  	if( nArg < 1 ){
 24481  		/* Missing arguments, return NULL */
 24482  		jx9_result_null(pCtx);
 24483  		return JX9_OK;
 24484  	}
 24485  	/* Create a new array */
 24486  	pArray = jx9_context_new_array(pCtx);
 24487  	if( pArray == 0 ){
 24488  		jx9_result_null(pCtx);
 24489  		return JX9_OK;
 24490  	}
 24491  	/* Point to the internal representation of the hashmap */
 24492  	pMap = (jx9_hashmap *)pArray->x.pOther;
 24493  	if( jx9_value_is_json_array(apArg[0])){
 24494  		/* Point to the internal representation of the source */
 24495  		jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
 24496  		/* Perform the copy */
 24497  		jx9HashmapDup(pSrc, pMap);
 24498  	}else{
 24499  		/* Simple insertion */
 24500  		jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]); 
 24501  	}
 24502  	/* Return the duplicated array */
 24503  	jx9_result_value(pCtx, pArray);
 24504  	return JX9_OK;
 24505  }
 24506  /*
 24507   * bool array_erase(array $source)
 24508   *  Remove all elements from a given array.
 24509   * Parameters
 24510   *  $source
 24511   *   Target array
 24512   * Return
 24513   *  TRUE on success. FALSE otherwise.
 24514   * Note
 24515   *  This function is a symisc eXtension.
 24516   */
 24517  static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24518  {
 24519  	jx9_hashmap *pMap;
 24520  	if( nArg < 1 ){
 24521  		/* Missing arguments */
 24522  		jx9_result_bool(pCtx, 0);
 24523  		return JX9_OK;
 24524  	}
 24525  	/* Point to the target hashmap */
 24526  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24527  	/* Erase */
 24528  	jx9HashmapRelease(pMap, FALSE);
 24529  	return JX9_OK;
 24530  }
 24531  /*
 24532   * array array_diff(array $array1, array $array2, ...)
 24533   *  Computes the difference of arrays.
 24534   * Parameters
 24535   *  $array1
 24536   *    The array to compare from
 24537   *  $array2
 24538   *    An array to compare against 
 24539   *  $...
 24540   *   More arrays to compare against
 24541   * Return
 24542   *  Returns an array containing all the entries from array1 that
 24543   *  are not present in any of the other arrays.
 24544   */
 24545  static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24546  {
 24547  	jx9_hashmap_node *pEntry;
 24548  	jx9_hashmap *pSrc, *pMap;
 24549  	jx9_value *pArray;
 24550  	jx9_value *pVal;
 24551  	sxi32 rc;
 24552  	sxu32 n;
 24553  	int i;
 24554  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 24555  		/* Missing arguments, return NULL */
 24556  		jx9_result_null(pCtx);
 24557  		return JX9_OK;
 24558  	}
 24559  	if( nArg == 1 ){
 24560  		/* Return the first array since we cannot perform a diff */
 24561  		jx9_result_value(pCtx, apArg[0]);
 24562  		return JX9_OK;
 24563  	}
 24564  	/* Create a new array */
 24565  	pArray = jx9_context_new_array(pCtx);
 24566  	if( pArray == 0 ){
 24567  		jx9_result_null(pCtx);
 24568  		return JX9_OK;
 24569  	}
 24570  	/* Point to the internal representation of the source hashmap */
 24571  	pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
 24572  	/* Perform the diff */
 24573  	pEntry = pSrc->pFirst;
 24574  	n = pSrc->nEntry;
 24575  	for(;;){
 24576  		if( n < 1 ){
 24577  			break;
 24578  		}
 24579  		/* Extract the node value */
 24580  		pVal = HashmapExtractNodeValue(pEntry);
 24581  		if( pVal ){
 24582  			for( i = 1 ; i < nArg ; i++ ){
 24583  				if( !jx9_value_is_json_array(apArg[i])) {
 24584  					/* ignore */
 24585  					continue;
 24586  				}
 24587  				/* Point to the internal representation of the hashmap */
 24588  				pMap = (jx9_hashmap *)apArg[i]->x.pOther;
 24589  				/* Perform the lookup */
 24590  				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
 24591  				if( rc == SXRET_OK ){
 24592  					/* Value exist */
 24593  					break;
 24594  				}
 24595  			}
 24596  			if( i >= nArg ){
 24597  				/* Perform the insertion */
 24598  				HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
 24599  			}
 24600  		}
 24601  		/* Point to the next entry */
 24602  		pEntry = pEntry->pPrev; /* Reverse link */
 24603  		n--;
 24604  	}
 24605  	/* Return the freshly created array */
 24606  	jx9_result_value(pCtx, pArray);
 24607  	return JX9_OK;
 24608  }
 24609  /*
 24610   * array array_intersect(array $array1 , array $array2, ...)
 24611   *  Computes the intersection of arrays.
 24612   * Parameters
 24613   *  $array1
 24614   *    The array to compare from
 24615   *  $array2
 24616   *    An array to compare against 
 24617   *  $...
 24618   *   More arrays to compare against
 24619   * Return
 24620   *  Returns an array containing all of the values in array1 whose values exist
 24621   *  in all of the parameters. .
 24622   * Note that NULL is returned on failure.
 24623   */
 24624  static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24625  {
 24626  	jx9_hashmap_node *pEntry;
 24627  	jx9_hashmap *pSrc, *pMap;
 24628  	jx9_value *pArray;
 24629  	jx9_value *pVal;
 24630  	sxi32 rc;
 24631  	sxu32 n;
 24632  	int i;
 24633  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 24634  		/* Missing arguments, return NULL */
 24635  		jx9_result_null(pCtx);
 24636  		return JX9_OK;
 24637  	}
 24638  	if( nArg == 1 ){
 24639  		/* Return the first array since we cannot perform a diff */
 24640  		jx9_result_value(pCtx, apArg[0]);
 24641  		return JX9_OK;
 24642  	}
 24643  	/* Create a new array */
 24644  	pArray = jx9_context_new_array(pCtx);
 24645  	if( pArray == 0 ){
 24646  		jx9_result_null(pCtx);
 24647  		return JX9_OK;
 24648  	}
 24649  	/* Point to the internal representation of the source hashmap */
 24650  	pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
 24651  	/* Perform the intersection */
 24652  	pEntry = pSrc->pFirst;
 24653  	n = pSrc->nEntry;
 24654  	for(;;){
 24655  		if( n < 1 ){
 24656  			break;
 24657  		}
 24658  		/* Extract the node value */
 24659  		pVal = HashmapExtractNodeValue(pEntry);
 24660  		if( pVal ){
 24661  			for( i = 1 ; i < nArg ; i++ ){
 24662  				if( !jx9_value_is_json_array(apArg[i])) {
 24663  					/* ignore */
 24664  					continue;
 24665  				}
 24666  				/* Point to the internal representation of the hashmap */
 24667  				pMap = (jx9_hashmap *)apArg[i]->x.pOther;
 24668  				/* Perform the lookup */
 24669  				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
 24670  				if( rc != SXRET_OK ){
 24671  					/* Value does not exist */
 24672  					break;
 24673  				}
 24674  			}
 24675  			if( i >= nArg ){
 24676  				/* Perform the insertion */
 24677  				HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
 24678  			}
 24679  		}
 24680  		/* Point to the next entry */
 24681  		pEntry = pEntry->pPrev; /* Reverse link */
 24682  		n--;
 24683  	}
 24684  	/* Return the freshly created array */
 24685  	jx9_result_value(pCtx, pArray);
 24686  	return JX9_OK;
 24687  }
 24688  /*
 24689   * number array_sum(array $array )
 24690   *  Calculate the sum of values in an array.
 24691   * Parameters
 24692   *  $array: The input array.
 24693   * Return
 24694   *  Returns the sum of values as an integer or float.
 24695   */
 24696  static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap)
 24697  {
 24698  	jx9_hashmap_node *pEntry;
 24699  	jx9_value *pObj;
 24700  	double dSum = 0;
 24701  	sxu32 n;
 24702  	pEntry = pMap->pFirst;
 24703  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24704  		pObj = HashmapExtractNodeValue(pEntry);
 24705  		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
 24706  			if( pObj->iFlags & MEMOBJ_REAL ){
 24707  				dSum += pObj->x.rVal;
 24708  			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 24709  				dSum += (double)pObj->x.iVal;
 24710  			}else if( pObj->iFlags & MEMOBJ_STRING ){
 24711  				if( SyBlobLength(&pObj->sBlob) > 0 ){
 24712  					double dv = 0;
 24713  					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
 24714  					dSum += dv;
 24715  				}
 24716  			}
 24717  		}
 24718  		/* Point to the next entry */
 24719  		pEntry = pEntry->pPrev; /* Reverse link */
 24720  	}
 24721  	/* Return sum */
 24722  	jx9_result_double(pCtx, dSum);
 24723  }
 24724  static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap)
 24725  {
 24726  	jx9_hashmap_node *pEntry;
 24727  	jx9_value *pObj;
 24728  	sxi64 nSum = 0;
 24729  	sxu32 n;
 24730  	pEntry = pMap->pFirst;
 24731  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24732  		pObj = HashmapExtractNodeValue(pEntry);
 24733  		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
 24734  			if( pObj->iFlags & MEMOBJ_REAL ){
 24735  				nSum += (sxi64)pObj->x.rVal;
 24736  			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 24737  				nSum += pObj->x.iVal;
 24738  			}else if( pObj->iFlags & MEMOBJ_STRING ){
 24739  				if( SyBlobLength(&pObj->sBlob) > 0 ){
 24740  					sxi64 nv = 0;
 24741  					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
 24742  					nSum += nv;
 24743  				}
 24744  			}
 24745  		}
 24746  		/* Point to the next entry */
 24747  		pEntry = pEntry->pPrev; /* Reverse link */
 24748  	}
 24749  	/* Return sum */
 24750  	jx9_result_int64(pCtx, nSum);
 24751  }
 24752  /* number array_sum(array $array ) 
 24753   * (See block-coment above)
 24754   */
 24755  static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24756  {
 24757  	jx9_hashmap *pMap;
 24758  	jx9_value *pObj;
 24759  	if( nArg < 1 ){
 24760  		/* Missing arguments, return 0 */
 24761  		jx9_result_int(pCtx, 0);
 24762  		return JX9_OK;
 24763  	}
 24764  	/* Make sure we are dealing with a valid hashmap */
 24765  	if( !jx9_value_is_json_array(apArg[0]) ){
 24766  		/* Invalid argument, return 0 */
 24767  		jx9_result_int(pCtx, 0);
 24768  		return JX9_OK;
 24769  	}
 24770  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24771  	if( pMap->nEntry < 1 ){
 24772  		/* Nothing to compute, return 0 */
 24773  		jx9_result_int(pCtx, 0);
 24774  		return JX9_OK;
 24775  	}
 24776  	/* If the first element is of type float, then perform floating
 24777  	 * point computaion.Otherwise switch to int64 computaion.
 24778  	 */
 24779  	pObj = HashmapExtractNodeValue(pMap->pFirst);
 24780  	if( pObj == 0 ){
 24781  		jx9_result_int(pCtx, 0);
 24782  		return JX9_OK;
 24783  	}
 24784  	if( pObj->iFlags & MEMOBJ_REAL ){
 24785  		DoubleSum(pCtx, pMap);
 24786  	}else{
 24787  		Int64Sum(pCtx, pMap);
 24788  	}
 24789  	return JX9_OK;
 24790  }
 24791  /*
 24792   * number array_product(array $array )
 24793   *  Calculate the product of values in an array.
 24794   * Parameters
 24795   *  $array: The input array.
 24796   * Return
 24797   *  Returns the product of values as an integer or float.
 24798   */
 24799  static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap)
 24800  {
 24801  	jx9_hashmap_node *pEntry;
 24802  	jx9_value *pObj;
 24803  	double dProd;
 24804  	sxu32 n;
 24805  	pEntry = pMap->pFirst;
 24806  	dProd = 1;
 24807  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24808  		pObj = HashmapExtractNodeValue(pEntry);
 24809  		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
 24810  			if( pObj->iFlags & MEMOBJ_REAL ){
 24811  				dProd *= pObj->x.rVal;
 24812  			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 24813  				dProd *= (double)pObj->x.iVal;
 24814  			}else if( pObj->iFlags & MEMOBJ_STRING ){
 24815  				if( SyBlobLength(&pObj->sBlob) > 0 ){
 24816  					double dv = 0;
 24817  					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
 24818  					dProd *= dv;
 24819  				}
 24820  			}
 24821  		}
 24822  		/* Point to the next entry */
 24823  		pEntry = pEntry->pPrev; /* Reverse link */
 24824  	}
 24825  	/* Return product */
 24826  	jx9_result_double(pCtx, dProd);
 24827  }
 24828  static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap)
 24829  {
 24830  	jx9_hashmap_node *pEntry;
 24831  	jx9_value *pObj;
 24832  	sxi64 nProd;
 24833  	sxu32 n;
 24834  	pEntry = pMap->pFirst;
 24835  	nProd = 1;
 24836  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24837  		pObj = HashmapExtractNodeValue(pEntry);
 24838  		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
 24839  			if( pObj->iFlags & MEMOBJ_REAL ){
 24840  				nProd *= (sxi64)pObj->x.rVal;
 24841  			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 24842  				nProd *= pObj->x.iVal;
 24843  			}else if( pObj->iFlags & MEMOBJ_STRING ){
 24844  				if( SyBlobLength(&pObj->sBlob) > 0 ){
 24845  					sxi64 nv = 0;
 24846  					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
 24847  					nProd *= nv;
 24848  				}
 24849  			}
 24850  		}
 24851  		/* Point to the next entry */
 24852  		pEntry = pEntry->pPrev; /* Reverse link */
 24853  	}
 24854  	/* Return product */
 24855  	jx9_result_int64(pCtx, nProd);
 24856  }
 24857  /* number array_product(array $array )
 24858   * (See block-block comment above)
 24859   */
 24860  static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24861  {
 24862  	jx9_hashmap *pMap;
 24863  	jx9_value *pObj;
 24864  	if( nArg < 1 ){
 24865  		/* Missing arguments, return 0 */
 24866  		jx9_result_int(pCtx, 0);
 24867  		return JX9_OK;
 24868  	}
 24869  	/* Make sure we are dealing with a valid hashmap */
 24870  	if( !jx9_value_is_json_array(apArg[0]) ){
 24871  		/* Invalid argument, return 0 */
 24872  		jx9_result_int(pCtx, 0);
 24873  		return JX9_OK;
 24874  	}
 24875  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24876  	if( pMap->nEntry < 1 ){
 24877  		/* Nothing to compute, return 0 */
 24878  		jx9_result_int(pCtx, 0);
 24879  		return JX9_OK;
 24880  	}
 24881  	/* If the first element is of type float, then perform floating
 24882  	 * point computaion.Otherwise switch to int64 computaion.
 24883  	 */
 24884  	pObj = HashmapExtractNodeValue(pMap->pFirst);
 24885  	if( pObj == 0 ){
 24886  		jx9_result_int(pCtx, 0);
 24887  		return JX9_OK;
 24888  	}
 24889  	if( pObj->iFlags & MEMOBJ_REAL ){
 24890  		DoubleProd(pCtx, pMap);
 24891  	}else{
 24892  		Int64Prod(pCtx, pMap);
 24893  	}
 24894  	return JX9_OK;
 24895  }
 24896  /*
 24897   * array array_map(callback $callback, array $arr1)
 24898   *  Applies the callback to the elements of the given arrays.
 24899   * Parameters
 24900   *  $callback
 24901   *   Callback function to run for each element in each array.
 24902   * $arr1
 24903   *   An array to run through the callback function.
 24904   * Return
 24905   *  Returns an array containing all the elements of arr1 after applying
 24906   *  the callback function to each one. 
 24907   * NOTE:
 24908   *  array_map() passes only a single value to the callback. 
 24909   */
 24910  static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24911  {
 24912  	jx9_value *pArray, *pValue, sKey, sResult;
 24913  	jx9_hashmap_node *pEntry;
 24914  	jx9_hashmap *pMap;
 24915  	sxu32 n;
 24916  	if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){
 24917  		/* Invalid arguments, return NULL */
 24918  		jx9_result_null(pCtx);
 24919  		return JX9_OK;
 24920  	}
 24921  	/* Create a new array */
 24922  	pArray = jx9_context_new_array(pCtx);
 24923  	if( pArray == 0 ){
 24924  		jx9_result_null(pCtx);
 24925  		return JX9_OK;
 24926  	}
 24927  	/* Point to the internal representation of the input hashmap */
 24928  	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
 24929  	jx9MemObjInit(pMap->pVm, &sResult);
 24930  	jx9MemObjInit(pMap->pVm, &sKey);
 24931  	/* Perform the requested operation */
 24932  	pEntry = pMap->pFirst;
 24933  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24934  		/* Extrcat the node value */
 24935  		pValue = HashmapExtractNodeValue(pEntry);
 24936  		if( pValue ){
 24937  			sxi32 rc;
 24938  			/* Invoke the supplied callback */
 24939  			rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
 24940  			/* Extract the node key */
 24941  			jx9HashmapExtractNodeKey(pEntry, &sKey);
 24942  			if( rc != SXRET_OK ){
 24943  				/* An error occured while invoking the supplied callback [i.e: not defined] */
 24944  				jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
 24945  			}else{
 24946  				/* Insert the callback return value */
 24947  				jx9_array_add_elem(pArray, &sKey, &sResult);
 24948  			}
 24949  			jx9MemObjRelease(&sKey);
 24950  			jx9MemObjRelease(&sResult);
 24951  		}
 24952  		/* Point to the next entry */
 24953  		pEntry = pEntry->pPrev; /* Reverse link */
 24954  	}
 24955  	jx9_result_value(pCtx, pArray);
 24956  	return JX9_OK;
 24957  }
 24958  /*
 24959   * bool array_walk(array &$array, callback $funcname [, value $userdata ] )
 24960   *  Apply a user function to every member of an array.
 24961   * Parameters
 24962   *  $array
 24963   *   The input array.
 24964   * $funcname
 24965   *  Typically, funcname takes on two parameters.The array parameter's value being
 24966   *  the first, and the key/index second.
 24967   * Note:
 24968   *  If funcname needs to be working with the actual values of the array, specify the first
 24969   *  parameter of funcname as a reference. Then, any changes made to those elements will 
 24970   *  be made in the original array itself.
 24971   * $userdata
 24972   *  If the optional userdata parameter is supplied, it will be passed as the third parameter
 24973   *  to the callback funcname.
 24974   * Return
 24975   *  Returns TRUE on success or FALSE on failure.
 24976   */
 24977  static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg)
 24978  {
 24979  	jx9_value *pValue, *pUserData, sKey;
 24980  	jx9_hashmap_node *pEntry;
 24981  	jx9_hashmap *pMap;
 24982  	sxi32 rc;
 24983  	sxu32 n;
 24984  	if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){
 24985  		/* Invalid/Missing arguments, return FALSE */
 24986  		jx9_result_bool(pCtx, 0);
 24987  		return JX9_OK;
 24988  	}
 24989  	pUserData = nArg > 2 ? apArg[2] : 0;
 24990  	/* Point to the internal representation of the input hashmap */
 24991  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 24992  	jx9MemObjInit(pMap->pVm, &sKey);
 24993  	/* Perform the desired operation */
 24994  	pEntry = pMap->pFirst;
 24995  	for( n = 0 ; n < pMap->nEntry ; n++ ){
 24996  		/* Extract the node value */
 24997  		pValue = HashmapExtractNodeValue(pEntry);
 24998  		if( pValue ){
 24999  			/* Extract the entry key */
 25000  			jx9HashmapExtractNodeKey(pEntry, &sKey);
 25001  			/* Invoke the supplied callback */
 25002  			rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
 25003  			jx9MemObjRelease(&sKey);
 25004  			if( rc != SXRET_OK ){
 25005  				/* An error occured while invoking the supplied callback [i.e: not defined] */
 25006  				jx9_result_bool(pCtx, 0); /* return FALSE */
 25007  				return JX9_OK;
 25008  			}
 25009  		}
 25010  		/* Point to the next entry */
 25011  		pEntry = pEntry->pPrev; /* Reverse link */
 25012  	}
 25013  	/* All done, return TRUE */
 25014  	jx9_result_bool(pCtx, 1);
 25015  	return JX9_OK;
 25016  }
 25017  /*
 25018   * Table of built-in hashmap functions.
 25019   */
 25020  static const jx9_builtin_func aHashmapFunc[] = {
 25021  	{"count",             jx9_hashmap_count }, 
 25022  	{"sizeof",            jx9_hashmap_count }, 
 25023  	{"array_key_exists",  jx9_hashmap_key_exists }, 
 25024  	{"array_pop",         jx9_hashmap_pop     }, 
 25025  	{"array_push",        jx9_hashmap_push    }, 
 25026  	{"array_shift",       jx9_hashmap_shift   }, 
 25027  	{"array_product",     jx9_hashmap_product }, 
 25028  	{"array_sum",         jx9_hashmap_sum     }, 
 25029  	{"array_values",      jx9_hashmap_values  }, 
 25030  	{"array_same",        jx9_hashmap_same    },
 25031  	{"array_merge",       jx9_hashmap_merge   }, 
 25032  	{"array_diff",        jx9_hashmap_diff    }, 
 25033  	{"array_intersect",   jx9_hashmap_intersect}, 
 25034  	{"in_array",          jx9_hashmap_in_array },
 25035  	{"array_copy",        jx9_hashmap_copy    }, 
 25036  	{"array_erase",       jx9_hashmap_erase   }, 
 25037  	{"array_map",         jx9_hashmap_map     }, 
 25038  	{"array_walk",        jx9_hashmap_walk    }, 
 25039  	{"sort",              jx9_hashmap_sort    }, 
 25040  	{"rsort",             jx9_hashmap_rsort   }, 
 25041  	{"usort",             jx9_hashmap_usort   }, 
 25042  	{"current",           jx9_hashmap_current }, 
 25043  	{"each",              jx9_hashmap_each    }, 
 25044  	{"pos",               jx9_hashmap_current }, 
 25045  	{"next",              jx9_hashmap_next    }, 
 25046  	{"prev",              jx9_hashmap_prev    }, 
 25047  	{"end",               jx9_hashmap_end     }, 
 25048  	{"reset",             jx9_hashmap_reset   }, 
 25049  	{"key",               jx9_hashmap_simple_key }
 25050  };
 25051  /*
 25052   * Register the built-in hashmap functions defined above.
 25053   */
 25054  JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm)
 25055  {
 25056  	sxu32 n;
 25057  	for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){
 25058  		jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
 25059  	}
 25060  }
 25061  /*
 25062   * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each 
 25063   * retrieved entry.
 25064   * Note that argument are passed to the callback by copy. That is, any modification to 
 25065   * the entry value in the callback body will not alter the real value.
 25066   * If the callback wishes to abort processing [i.e: it's invocation] it must return
 25067   * a value different from JX9_OK.
 25068   * Refer to [jx9_array_walk()] for more information.
 25069   */
 25070  JX9_PRIVATE sxi32 jx9HashmapWalk(
 25071  	jx9_hashmap *pMap, /* Target hashmap */
 25072  	int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */
 25073  	void *pUserData /* Last argument to xWalk() */
 25074  	)
 25075  {
 25076  	jx9_hashmap_node *pEntry;
 25077  	jx9_value sKey, sValue;
 25078  	sxi32 rc;
 25079  	sxu32 n;
 25080  	/* Initialize walker parameter */
 25081  	rc = SXRET_OK;
 25082  	jx9MemObjInit(pMap->pVm, &sKey);
 25083  	jx9MemObjInit(pMap->pVm, &sValue);
 25084  	n = pMap->nEntry;
 25085  	pEntry = pMap->pFirst;
 25086  	/* Start the iteration process */
 25087  	for(;;){
 25088  		if( n < 1 ){
 25089  			break;
 25090  		}
 25091  		/* Extract a copy of the key and a copy the current value */
 25092  		jx9HashmapExtractNodeKey(pEntry, &sKey);
 25093  		jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE);
 25094  		/* Invoke the user callback */
 25095  		rc = xWalk(&sKey, &sValue, pUserData);
 25096  		/* Release the copy of the key and the value */
 25097  		jx9MemObjRelease(&sKey);
 25098  		jx9MemObjRelease(&sValue);
 25099  		if( rc != JX9_OK ){
 25100  			/* Callback request an operation abort */
 25101  			return SXERR_ABORT;
 25102  		}
 25103  		/* Point to the next entry */
 25104  		pEntry = pEntry->pPrev; /* Reverse link */
 25105  		n--;
 25106  	}
 25107  	/* All done */
 25108  	return SXRET_OK;
 25109  }
 25110  /*
 25111   * ----------------------------------------------------------
 25112   * File: jx9_json.c
 25113   * MD5: 31a27f8797418de511c669feed763341
 25114   * ----------------------------------------------------------
 25115   */
 25116  /*
 25117   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 25118   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 25119   * Version 1.7.2
 25120   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 25121   * please contact Symisc Systems via:
 25122   *       legal@symisc.net
 25123   *       licensing@symisc.net
 25124   *       contact@symisc.net
 25125   * or visit:
 25126   *      http://jx9.symisc.net/
 25127   */
 25128   /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
 25129  #ifndef JX9_AMALGAMATION
 25130  #include "jx9Int.h"
 25131  #endif
 25132  /* This file deals with JSON serialization, decoding and stuff like that. */
 25133  /*
 25134   * Section: 
 25135   *  JSON encoding/decoding routines.
 25136   * Authors:
 25137   *  Symisc Systems, devel@symisc.net.
 25138   *  Copyright (C) Symisc Systems, http://jx9.symisc.net
 25139   * Status:
 25140   *    Devel.
 25141   */
 25142  /* Forward reference */
 25143  static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
 25144  static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
 25145  /* 
 25146   * JSON encoder state is stored in an instance 
 25147   * of the following structure.
 25148   */
 25149  typedef struct json_private_data json_private_data;
 25150  struct json_private_data
 25151  {
 25152  	SyBlob *pOut;      /* Output consumer buffer */
 25153  	int isFirst;       /* True if first encoded entry */
 25154  	int iFlags;        /* JSON encoding flags */
 25155  	int nRecCount;     /* Recursion count */
 25156  };
 25157  /*
 25158   * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
 25159   * According to wikipedia
 25160   * JSON's basic types are:
 25161   *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
 25162   *   String (double-quoted Unicode, with backslash escaping)
 25163   *   Boolean (true or false)
 25164   *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
 25165   *    do not need to be of the same type)
 25166   *   Object (an unordered collection of key:value pairs with the ':' character separating the key 
 25167   *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
 25168   *     be distinct from each other)
 25169   *   null (empty)
 25170   * Non-significant white space may be added freely around the "structural characters"
 25171   * (i.e. the brackets "[{]}", colon ":" and comma ",").
 25172   */
 25173  static sxi32 VmJsonEncode(
 25174  	jx9_value *pIn,          /* Encode this value */
 25175  	json_private_data *pData /* Context data */
 25176  	){
 25177  		SyBlob *pOut = pData->pOut;
 25178  		int nByte;
 25179  		if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
 25180  			/* null */
 25181  			SyBlobAppend(pOut, "null", sizeof("null")-1);
 25182  		}else if( jx9_value_is_bool(pIn) ){
 25183  			int iBool = jx9_value_to_bool(pIn);
 25184  			sxu32 iLen;
 25185  			/* true/false */
 25186  			iLen = iBool ? sizeof("true") : sizeof("false");
 25187  			SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
 25188  		}else if(  jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
 25189  			const char *zNum;
 25190  			/* Get a string representation of the number */
 25191  			zNum = jx9_value_to_string(pIn, &nByte);
 25192  			SyBlobAppend(pOut,zNum,nByte);
 25193  		}else if( jx9_value_is_string(pIn) ){
 25194  				const char *zIn, *zEnd;
 25195  				int c;
 25196  				/* Encode the string */
 25197  				zIn = jx9_value_to_string(pIn, &nByte);
 25198  				zEnd = &zIn[nByte];
 25199  				/* Append the double quote */
 25200  				SyBlobAppend(pOut,"\"", sizeof(char));
 25201  				for(;;){
 25202  					if( zIn >= zEnd ){
 25203  						/* No more input to process */
 25204  						break;
 25205  					}
 25206  					c = zIn[0];
 25207  					/* Advance the stream cursor */
 25208  					zIn++;
 25209  					if( c == '"' || c == '\\' ){
 25210  						/* Unescape the character */
 25211  						SyBlobAppend(pOut,"\\", sizeof(char));
 25212  					}
 25213  					/* Append character verbatim */
 25214  					SyBlobAppend(pOut,(const char *)&c,sizeof(char));
 25215  				}
 25216  				/* Append the double quote */
 25217  				SyBlobAppend(pOut,"\"",sizeof(char));
 25218  		}else if( jx9_value_is_json_array(pIn) ){
 25219  			/* Encode the array/object */
 25220  			pData->isFirst = 1;
 25221  			if( jx9_value_is_json_object(pIn) ){
 25222  				/* Encode the object instance */
 25223  				pData->isFirst = 1;
 25224  				/* Append the curly braces */
 25225  				SyBlobAppend(pOut, "{",sizeof(char));
 25226  				/* Iterate throw object attribute */
 25227  				jx9_array_walk(pIn,VmJsonObjectEncode, pData);
 25228  				/* Append the closing curly braces  */
 25229  				SyBlobAppend(pOut, "}",sizeof(char));
 25230  			}else{
 25231  				/* Append the square bracket or curly braces */
 25232  				SyBlobAppend(pOut,"[",sizeof(char));
 25233  				/* Iterate throw array entries */
 25234  				jx9_array_walk(pIn, VmJsonArrayEncode, pData);
 25235  				/* Append the closing square bracket or curly braces */
 25236  				SyBlobAppend(pOut,"]",sizeof(char));
 25237  			}
 25238  		}else{
 25239  			/* Can't happen */
 25240  			SyBlobAppend(pOut,"null",sizeof("null")-1);
 25241  		}
 25242  		/* All done */
 25243  		return JX9_OK;
 25244  }
 25245  /*
 25246   * The following walker callback is invoked each time we need
 25247   * to encode an array to JSON.
 25248   */
 25249  static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
 25250  {
 25251  	json_private_data *pJson = (json_private_data *)pUserData;
 25252  	if( pJson->nRecCount > 31 ){
 25253  		/* Recursion limit reached, return immediately */
 25254  		SXUNUSED(pKey); /* cc warning */
 25255  		return JX9_OK;
 25256  	}
 25257  	if( !pJson->isFirst ){
 25258  		/* Append the colon first */
 25259  		SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
 25260  	}
 25261  	/* Encode the value */
 25262  	pJson->nRecCount++;
 25263  	VmJsonEncode(pValue, pJson);
 25264  	pJson->nRecCount--;
 25265  	pJson->isFirst = 0;
 25266  	return JX9_OK;
 25267  }
 25268  /*
 25269   * The following walker callback is invoked each time we need to encode
 25270   * a object instance [i.e: Object in the JX9 jargon] to JSON.
 25271   */
 25272  static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
 25273  {
 25274  	json_private_data *pJson = (json_private_data *)pUserData;
 25275  	const char *zKey;
 25276  	int nByte;
 25277  	if( pJson->nRecCount > 31 ){
 25278  		/* Recursion limit reached, return immediately */
 25279  		return JX9_OK;
 25280  	}
 25281  	if( !pJson->isFirst ){
 25282  		/* Append the colon first */
 25283  		SyBlobAppend(pJson->pOut,",",sizeof(char));
 25284  	}
 25285  	/* Extract a string representation of the key */
 25286  	zKey = jx9_value_to_string(pKey, &nByte);
 25287  	/* Append the key and the double colon */
 25288  	if( nByte > 0 ){
 25289  		SyBlobAppend(pJson->pOut,"\"",sizeof(char));
 25290  		SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
 25291  		SyBlobAppend(pJson->pOut,"\"",sizeof(char));
 25292  	}else{
 25293  		/* Can't happen */
 25294  		SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
 25295  	}
 25296  	SyBlobAppend(pJson->pOut,":",sizeof(char));
 25297  	/* Encode the value */
 25298  	pJson->nRecCount++;
 25299  	VmJsonEncode(pValue, pJson);
 25300  	pJson->nRecCount--;
 25301  	pJson->isFirst = 0;
 25302  	return JX9_OK;
 25303  }
 25304  /*
 25305   *  Returns a string containing the JSON representation of value. 
 25306   *  In other words, perform the serialization of the given JSON object.
 25307   */
 25308  JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
 25309  {
 25310  	json_private_data sJson;
 25311  	/* Prepare the JSON data */
 25312  	sJson.nRecCount = 0;
 25313  	sJson.pOut = pOut;
 25314  	sJson.isFirst = 1;
 25315  	sJson.iFlags = 0;
 25316  	/* Perform the encoding operation */
 25317  	VmJsonEncode(pValue, &sJson);
 25318  	/* All done */
 25319  	return JX9_OK;
 25320  }
 25321  /* Possible tokens from the JSON tokenization process */
 25322  #define JSON_TK_TRUE    0x001 /* Boolean true */
 25323  #define JSON_TK_FALSE   0x002 /* Boolean false */
 25324  #define JSON_TK_STR     0x004 /* String enclosed in double quotes */
 25325  #define JSON_TK_NULL    0x008 /* null */
 25326  #define JSON_TK_NUM     0x010 /* Numeric */
 25327  #define JSON_TK_OCB     0x020 /* Open curly braces '{' */
 25328  #define JSON_TK_CCB     0x040 /* Closing curly braces '}' */
 25329  #define JSON_TK_OSB     0x080 /* Open square bracke '[' */
 25330  #define JSON_TK_CSB     0x100 /* Closing square bracket ']' */
 25331  #define JSON_TK_COLON   0x200 /* Single colon ':' */
 25332  #define JSON_TK_COMMA   0x400 /* Single comma ',' */
 25333  #define JSON_TK_ID      0x800 /* ID */
 25334  #define JSON_TK_INVALID 0x1000 /* Unexpected token */
 25335  /* 
 25336   * Tokenize an entire JSON input.
 25337   * Get a single low-level token from the input file.
 25338   * Update the stream pointer so that it points to the first
 25339   * character beyond the extracted token.
 25340   */
 25341  static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
 25342  {
 25343  	int *pJsonErr = (int *)pUserData;
 25344  	SyString *pStr;
 25345  	int c;
 25346  	/* Ignore leading white spaces */
 25347  	while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
 25348  		/* Advance the stream cursor */
 25349  		if( pStream->zText[0] == '\n' ){
 25350  			/* Update line counter */
 25351  			pStream->nLine++;
 25352  		}
 25353  		pStream->zText++;
 25354  	}
 25355  	if( pStream->zText >= pStream->zEnd ){
 25356  		/* End of input reached */
 25357  		SXUNUSED(pCtxData); /* cc warning */
 25358  		return SXERR_EOF;
 25359  	}
 25360  	/* Record token starting position and line */
 25361  	pToken->nLine = pStream->nLine;
 25362  	pToken->pUserData = 0;
 25363  	pStr = &pToken->sData;
 25364  	SyStringInitFromBuf(pStr, pStream->zText, 0);
 25365  	if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
 25366  		/* The following code fragment is taken verbatim from the xPP source tree.
 25367  		 * xPP is a modern embeddable macro processor with advanced features useful for
 25368  		 * application seeking for a production quality, ready to use macro processor.
 25369  		 * xPP is a widely used library developed and maintened by Symisc Systems.
 25370  		 * You can reach the xPP home page by following this link:
 25371  		 * http://xpp.symisc.net/
 25372  		 */
 25373  		const unsigned char *zIn;
 25374  		/* Isolate UTF-8 or alphanumeric stream */
 25375  		if( pStream->zText[0] < 0xc0 ){
 25376  			pStream->zText++;
 25377  		}
 25378  		for(;;){
 25379  			zIn = pStream->zText;
 25380  			if( zIn[0] >= 0xc0 ){
 25381  				zIn++;
 25382  				/* UTF-8 stream */
 25383  				while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
 25384  					zIn++;
 25385  				}
 25386  			}
 25387  			/* Skip alphanumeric stream */
 25388  			while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
 25389  				zIn++;
 25390  			}
 25391  			if( zIn == pStream->zText ){
 25392  				/* Not an UTF-8 or alphanumeric stream */
 25393  				break;
 25394  			}
 25395  			/* Synchronize pointers */
 25396  			pStream->zText = zIn;
 25397  		}
 25398  		/* Record token length */
 25399  		pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 25400  		/* A simple identifier */
 25401  		pToken->nType = JSON_TK_ID;
 25402  		if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
 25403  			/* boolean true */
 25404  			pToken->nType = JSON_TK_TRUE;
 25405  		}else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
 25406  			/* boolean false */
 25407  			pToken->nType = JSON_TK_FALSE;
 25408  		}else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
 25409  			/* NULL */
 25410  			pToken->nType = JSON_TK_NULL;
 25411  		}
 25412  		return SXRET_OK;
 25413  	}
 25414  	if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
 25415  		|| pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
 25416  			/* Single character */
 25417  			c = pStream->zText[0];
 25418  			/* Set token type */
 25419  			switch(c){
 25420  			case '[': pToken->nType = JSON_TK_OSB;   break;
 25421  			case '{': pToken->nType = JSON_TK_OCB;   break;
 25422  			case '}': pToken->nType = JSON_TK_CCB;   break;
 25423  			case ']': pToken->nType = JSON_TK_CSB;   break;
 25424  			case ':': pToken->nType = JSON_TK_COLON; break;
 25425  			case ',': pToken->nType = JSON_TK_COMMA; break;
 25426  			default:
 25427  				break;
 25428  			}
 25429  			/* Advance the stream cursor */
 25430  			pStream->zText++;
 25431  	}else if( pStream->zText[0] == '"') {
 25432  		/* JSON string */
 25433  		pStream->zText++;
 25434  		pStr->zString++;
 25435  		/* Delimit the string */
 25436  		while( pStream->zText < pStream->zEnd ){
 25437  			if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
 25438  				break;
 25439  			}
 25440  			if( pStream->zText[0] == '\n' ){
 25441  				/* Update line counter */
 25442  				pStream->nLine++;
 25443  			}
 25444  			pStream->zText++;
 25445  		}
 25446  		if( pStream->zText >= pStream->zEnd ){
 25447  			/* Missing closing '"' */
 25448  			pToken->nType = JSON_TK_INVALID;
 25449  			*pJsonErr = SXERR_SYNTAX;
 25450  		}else{
 25451  			pToken->nType = JSON_TK_STR;
 25452  			pStream->zText++; /* Jump the closing double quotes */
 25453  		}
 25454  	}else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25455  		/* Number */
 25456  		pStream->zText++;
 25457  		pToken->nType = JSON_TK_NUM;
 25458  		while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25459  			pStream->zText++;
 25460  		}
 25461  		if( pStream->zText < pStream->zEnd ){
 25462  			c = pStream->zText[0];
 25463  			if( c == '.' ){
 25464  					/* Real number */
 25465  					pStream->zText++;
 25466  					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25467  						pStream->zText++;
 25468  					}
 25469  					if( pStream->zText < pStream->zEnd ){
 25470  						c = pStream->zText[0];
 25471  						if( c=='e' || c=='E' ){
 25472  							pStream->zText++;
 25473  							if( pStream->zText < pStream->zEnd ){
 25474  								c = pStream->zText[0];
 25475  								if( c =='+' || c=='-' ){
 25476  									pStream->zText++;
 25477  								}
 25478  								while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25479  									pStream->zText++;
 25480  								}
 25481  							}
 25482  						}
 25483  					}					
 25484  				}else if( c=='e' || c=='E' ){
 25485  					/* Real number */
 25486  					pStream->zText++;
 25487  					if( pStream->zText < pStream->zEnd ){
 25488  						c = pStream->zText[0];
 25489  						if( c =='+' || c=='-' ){
 25490  							pStream->zText++;
 25491  						}
 25492  						while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25493  							pStream->zText++;
 25494  						}
 25495  					}					
 25496  				}
 25497  			}
 25498  	}else{
 25499  		/* Unexpected token */
 25500  		pToken->nType = JSON_TK_INVALID;
 25501  		/* Advance the stream cursor */
 25502  		pStream->zText++;
 25503  		*pJsonErr = SXERR_SYNTAX;
 25504  		/* Abort processing immediatley */
 25505  		return SXERR_ABORT;
 25506  	}
 25507  	/* record token length */
 25508  	pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 25509  	if( pToken->nType == JSON_TK_STR ){
 25510  		pStr->nByte--;
 25511  	}
 25512  	/* Return to the lexer */
 25513  	return SXRET_OK;
 25514  }
 25515  /*
 25516   * JSON decoded input consumer callback signature.
 25517   */
 25518  typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *); 
 25519  /* 
 25520   * JSON decoder state is kept in the following structure.
 25521   */
 25522  typedef struct json_decoder json_decoder;
 25523  struct json_decoder
 25524  {
 25525  	jx9_context *pCtx; /* Call context */
 25526  	ProcJSONConsumer xConsumer; /* Consumer callback */ 
 25527  	void *pUserData;   /* Last argument to xConsumer() */
 25528  	int iFlags;        /* Configuration flags */
 25529  	SyToken *pIn;      /* Token stream */
 25530  	SyToken *pEnd;     /* End of the token stream */
 25531  	int rec_count;     /* Current nesting level */
 25532  	int *pErr;         /* JSON decoding error if any */
 25533  };
 25534  /* Forward declaration */
 25535  static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
 25536  /*
 25537   * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
 25538   * the result in the given jx9_value.
 25539   */
 25540  static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
 25541  {
 25542  	const char *zIn = pStr->zString;
 25543  	const char *zEnd = &pStr->zString[pStr->nByte];
 25544  	const char *zCur;
 25545  	int c;
 25546  	/* Mark the value as a string */
 25547  	jx9_value_string(pWorker, "", 0); /* Empty string */
 25548  	for(;;){
 25549  		zCur = zIn;
 25550  		while( zIn < zEnd && zIn[0] != '\\' ){
 25551  			zIn++;
 25552  		}
 25553  		if( zIn > zCur ){
 25554  			/* Append chunk verbatim */
 25555  			jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
 25556  		}
 25557  		zIn++;
 25558  		if( zIn >= zEnd ){
 25559  			/* End of the input reached */
 25560  			break;
 25561  		}
 25562  		c = zIn[0];
 25563  		/* Unescape the character */
 25564  		switch(c){
 25565  		case '"':  jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
 25566  		case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
 25567  		case 'n':  jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
 25568  		case 'r':  jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
 25569  		case 't':  jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
 25570  		case 'f':  jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
 25571  		default:
 25572  			jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
 25573  			break;
 25574  		}
 25575  		/* Advance the stream cursor */
 25576  		zIn++;
 25577  	}
 25578  }
 25579  /*
 25580   * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
 25581   * According to wikipedia
 25582   * JSON's basic types are:
 25583   *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
 25584   *   String (double-quoted Unicode, with backslash escaping)
 25585   *   Boolean (true or false)
 25586   *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
 25587   *    do not need to be of the same type)
 25588   *   Object (an unordered collection of key:value pairs with the ':' character separating the key 
 25589   *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
 25590   *     be distinct from each other)
 25591   *   null (empty)
 25592   * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
 25593   */
 25594  static sxi32 VmJsonDecode(
 25595  	json_decoder *pDecoder, /* JSON decoder */      
 25596  	jx9_value *pArrayKey    /* Key for the decoded array */
 25597  	){
 25598  	jx9_value *pWorker; /* Worker variable */
 25599  	sxi32 rc;
 25600  	/* Check if we do not nest to much */
 25601  	if( pDecoder->rec_count > 31 ){
 25602  		/* Nesting limit reached, abort decoding immediately */
 25603  		return SXERR_ABORT;
 25604  	}
 25605  	if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
 25606  		/* Scalar value */
 25607  		pWorker = jx9_context_new_scalar(pDecoder->pCtx);
 25608  		if( pWorker == 0 ){
 25609  			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 25610  			/* Abort the decoding operation immediately */
 25611  			return SXERR_ABORT;
 25612  		}
 25613  		/* Reflect the JSON image */
 25614  		if( pDecoder->pIn->nType & JSON_TK_NULL ){
 25615  			/* Nullify the value.*/
 25616  			jx9_value_null(pWorker);
 25617  		}else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
 25618  			/* Boolean value */
 25619  			jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
 25620  		}else if( pDecoder->pIn->nType & JSON_TK_NUM ){
 25621  			SyString *pStr = &pDecoder->pIn->sData;
 25622  			/* 
 25623  			 * Numeric value.
 25624  			 * Get a string representation first then try to get a numeric
 25625  			 * value.
 25626  			 */
 25627  			jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
 25628  			/* Obtain a numeric representation */
 25629  			jx9MemObjToNumeric(pWorker);
 25630  		}else if( pDecoder->pIn->nType & JSON_TK_ID ){
 25631  			SyString *pStr = &pDecoder->pIn->sData;
 25632  			jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
 25633  		}else{
 25634  			/* Dequote the string */
 25635  			VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
 25636  		}
 25637  		/* Invoke the consumer callback */
 25638  		rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
 25639  		if( rc == SXERR_ABORT ){
 25640  			return SXERR_ABORT;
 25641  		}
 25642  		/* All done, advance the stream cursor */
 25643  		pDecoder->pIn++;
 25644  	}else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
 25645  		ProcJSONConsumer xOld;
 25646  		void *pOld;
 25647  		/* Array representation*/
 25648  		pDecoder->pIn++;
 25649  		/* Create a working array */
 25650  		pWorker = jx9_context_new_array(pDecoder->pCtx);
 25651  		if( pWorker == 0 ){
 25652  			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 25653  			/* Abort the decoding operation immediately */
 25654  			return SXERR_ABORT;
 25655  		}
 25656  		/* Save the old consumer */
 25657  		xOld = pDecoder->xConsumer;
 25658  		pOld = pDecoder->pUserData;
 25659  		/* Set the new consumer */
 25660  		pDecoder->xConsumer = VmJsonArrayDecoder;
 25661  		pDecoder->pUserData = pWorker;
 25662  		/* Decode the array */
 25663  		for(;;){
 25664  			/* Jump trailing comma. Note that the standard JX9 engine will not let you
 25665  			 * do this.
 25666  			 */
 25667  			while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
 25668  				pDecoder->pIn++;
 25669  			}
 25670  			if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
 25671  				if( pDecoder->pIn < pDecoder->pEnd ){
 25672  					pDecoder->pIn++; /* Jump the trailing ']' */
 25673  				}
 25674  				break;
 25675  			}
 25676  			/* Recurse and decode the entry */
 25677  			pDecoder->rec_count++;
 25678  			rc = VmJsonDecode(pDecoder, 0);
 25679  			pDecoder->rec_count--;
 25680  			if( rc == SXERR_ABORT ){
 25681  				/* Abort processing immediately */
 25682  				return SXERR_ABORT;
 25683  			}
 25684  			/*The cursor is automatically advanced by the VmJsonDecode() function */
 25685  			if( (pDecoder->pIn < pDecoder->pEnd) &&
 25686  				((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
 25687  					/* Unexpected token, abort immediatley */
 25688  					*pDecoder->pErr = SXERR_SYNTAX;
 25689  					return SXERR_ABORT;
 25690  			}
 25691  		}
 25692  		/* Restore the old consumer */
 25693  		pDecoder->xConsumer = xOld;
 25694  		pDecoder->pUserData = pOld;
 25695  		/* Invoke the old consumer on the decoded array */
 25696  		xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
 25697  	}else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
 25698  		ProcJSONConsumer xOld;
 25699  		jx9_value *pKey;
 25700  		void *pOld;
 25701  		/* Object representation*/
 25702  		pDecoder->pIn++;
 25703  		/* Create a working array */
 25704  		pWorker = jx9_context_new_array(pDecoder->pCtx);
 25705  		pKey = jx9_context_new_scalar(pDecoder->pCtx);
 25706  		if( pWorker == 0 || pKey == 0){
 25707  			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 25708  			/* Abort the decoding operation immediately */
 25709  			return SXERR_ABORT;
 25710  		}
 25711  		/* Save the old consumer */
 25712  		xOld = pDecoder->xConsumer;
 25713  		pOld = pDecoder->pUserData;
 25714  		/* Set the new consumer */
 25715  		pDecoder->xConsumer = VmJsonArrayDecoder;
 25716  		pDecoder->pUserData = pWorker;
 25717  		/* Decode the object */
 25718  		for(;;){
 25719  			/* Jump trailing comma. Note that the standard JX9 engine will not let you
 25720  			 * do this.
 25721  			 */
 25722  			while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
 25723  				pDecoder->pIn++;
 25724  			}
 25725  			if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
 25726  				if( pDecoder->pIn < pDecoder->pEnd ){
 25727  					pDecoder->pIn++; /* Jump the trailing ']' */
 25728  				}
 25729  				break;
 25730  			}
 25731  			if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
 25732  				|| (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
 25733  					/* Syntax error, return immediately */
 25734  					*pDecoder->pErr = SXERR_SYNTAX;
 25735  					return SXERR_ABORT;
 25736  			}
 25737  			if( pDecoder->pIn->nType & JSON_TK_ID ){
 25738  				SyString *pStr = &pDecoder->pIn->sData;
 25739  				jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
 25740  			}else{
 25741  				/* Dequote the key */
 25742  				VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
 25743  			}
 25744  			/* Jump the key and the colon */
 25745  			pDecoder->pIn += 2; 
 25746  			/* Recurse and decode the value */
 25747  			pDecoder->rec_count++;
 25748  			rc = VmJsonDecode(pDecoder, pKey);
 25749  			pDecoder->rec_count--;
 25750  			if( rc == SXERR_ABORT ){
 25751  				/* Abort processing immediately */
 25752  				return SXERR_ABORT;
 25753  			}
 25754  			/* Reset the internal buffer of the key */
 25755  			jx9_value_reset_string_cursor(pKey);
 25756  			/*The cursor is automatically advanced by the VmJsonDecode() function */
 25757  		}
 25758  		/* Restore the old consumer */
 25759  		pDecoder->xConsumer = xOld;
 25760  		pDecoder->pUserData = pOld;
 25761  		/* Invoke the old consumer on the decoded object*/
 25762  		xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
 25763  		/* Release the key */
 25764  		jx9_context_release_value(pDecoder->pCtx, pKey);
 25765  	}else{
 25766  		/* Unexpected token */
 25767  		return SXERR_ABORT; /* Abort immediately */
 25768  	}
 25769  	/* Release the worker variable */
 25770  	jx9_context_release_value(pDecoder->pCtx, pWorker);
 25771  	return SXRET_OK;
 25772  }
 25773  /*
 25774   * The following JSON decoder callback is invoked each time
 25775   * a JSON array representation [i.e: [15, "hello", FALSE] ]
 25776   * is being decoded.
 25777   */
 25778  static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
 25779  {
 25780  	jx9_value *pArray = (jx9_value *)pUserData;
 25781  	/* Insert the entry */
 25782  	jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
 25783  	SXUNUSED(pCtx); /* cc warning */
 25784  	/* All done */
 25785  	return SXRET_OK;
 25786  }
 25787  /*
 25788   * Standard JSON decoder callback.
 25789   */
 25790  static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
 25791  {
 25792  	/* Return the value directly */
 25793  	jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
 25794  	SXUNUSED(pKey); /* cc warning */
 25795  	SXUNUSED(pUserData);
 25796  	/* All done */
 25797  	return SXRET_OK;
 25798  }
 25799  /*
 25800   * Exported JSON decoding interface
 25801   */
 25802  JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
 25803  {
 25804  	jx9_vm *pVm = pCtx->pVm;
 25805  	json_decoder sDecoder;
 25806  	SySet sToken;
 25807  	SyLex sLex;
 25808  	sxi32 rc;
 25809  	/* Tokenize the input */
 25810  	SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
 25811  	rc = SXRET_OK;
 25812  	SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
 25813  	SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
 25814  	if( rc != SXRET_OK ){
 25815  		/* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
 25816  		SyLexRelease(&sLex);
 25817  		SySetRelease(&sToken);
 25818  		/* return NULL */
 25819  		jx9_result_null(pCtx);
 25820  		return JX9_OK;
 25821  	}
 25822  	/* Fill the decoder */
 25823  	sDecoder.pCtx = pCtx;
 25824  	sDecoder.pErr = &rc;
 25825  	sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
 25826  	sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
 25827  	sDecoder.iFlags = 0;
 25828  	sDecoder.rec_count = 0;
 25829  	/* Set a default consumer */
 25830  	sDecoder.xConsumer = VmJsonDefaultDecoder;
 25831  	sDecoder.pUserData = 0;
 25832  	/* Decode the raw JSON input */
 25833  	rc = VmJsonDecode(&sDecoder, 0);
 25834  	if( rc == SXERR_ABORT ){
 25835  		/* 
 25836  		 * Something goes wrong while decoding JSON input.Return NULL.
 25837  		 */
 25838  		jx9_result_null(pCtx);
 25839  	}
 25840  	/* Clean-up the mess left behind */
 25841  	SyLexRelease(&sLex);
 25842  	SySetRelease(&sToken);
 25843  	/* All done */
 25844  	return JX9_OK;
 25845  }
 25846  /*
 25847   * ----------------------------------------------------------
 25848   * File: jx9_lex.c
 25849   * MD5: a79518c0635dbaf5dcfaca62efa2faf8
 25850   * ----------------------------------------------------------
 25851   */
 25852  /*
 25853   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 25854   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 25855   * Version 1.7.2
 25856   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 25857   * please contact Symisc Systems via:
 25858   *       legal@symisc.net
 25859   *       licensing@symisc.net
 25860   *       contact@symisc.net
 25861   * or visit:
 25862   *      http://jx9.symisc.net/
 25863   */
 25864   /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
 25865  #ifndef JX9_AMALGAMATION
 25866  #include "jx9Int.h"
 25867  #endif
 25868  /* This file implements a thread-safe and full reentrant lexical analyzer for the Jx9 programming language */
 25869  /* Forward declarations */
 25870  static sxu32 keywordCode(const char *z,int n);
 25871  static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken);
 25872  /*
 25873   * Tokenize a raw jx9 input.
 25874   * Get a single low-level token from the input file. Update the stream pointer so that
 25875   * it points to the first character beyond the extracted token.
 25876   */
 25877  static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData)
 25878  {
 25879  	SyString *pStr;
 25880  	sxi32 rc;
 25881  	/* Ignore leading white spaces */
 25882  	while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
 25883  		/* Advance the stream cursor */
 25884  		if( pStream->zText[0] == '\n' ){
 25885  			/* Update line counter */
 25886  			pStream->nLine++;
 25887  		}
 25888  		pStream->zText++;
 25889  	}
 25890  	if( pStream->zText >= pStream->zEnd ){
 25891  		/* End of input reached */
 25892  		return SXERR_EOF;
 25893  	}
 25894  	/* Record token starting position and line */
 25895  	pToken->nLine = pStream->nLine;
 25896  	pToken->pUserData = 0;
 25897  	pStr = &pToken->sData;
 25898  	SyStringInitFromBuf(pStr, pStream->zText, 0);
 25899  	if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
 25900  		/* The following code fragment is taken verbatim from the xPP source tree.
 25901  		 * xPP is a modern embeddable macro processor with advanced features useful for
 25902  		 * application seeking for a production quality, ready to use macro processor.
 25903  		 * xPP is a widely used library developed and maintened by Symisc Systems.
 25904  		 * You can reach the xPP home page by following this link:
 25905  		 * http://xpp.symisc.net/
 25906  		 */
 25907  		const unsigned char *zIn;
 25908  		sxu32 nKeyword;
 25909  		/* Isolate UTF-8 or alphanumeric stream */
 25910  		if( pStream->zText[0] < 0xc0 ){
 25911  			pStream->zText++;
 25912  		}
 25913  		for(;;){
 25914  			zIn = pStream->zText;
 25915  			if( zIn[0] >= 0xc0 ){
 25916  				zIn++;
 25917  				/* UTF-8 stream */
 25918  				while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
 25919  					zIn++;
 25920  				}
 25921  			}
 25922  			/* Skip alphanumeric stream */
 25923  			while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
 25924  				zIn++;
 25925  			}
 25926  			if( zIn == pStream->zText ){
 25927  				/* Not an UTF-8 or alphanumeric stream */
 25928  				break;
 25929  			}
 25930  			/* Synchronize pointers */
 25931  			pStream->zText = zIn;
 25932  		}
 25933  		/* Record token length */
 25934  		pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 25935  		nKeyword = keywordCode(pStr->zString, (int)pStr->nByte);
 25936  		if( nKeyword != JX9_TK_ID ){
 25937  			/* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */
 25938  			pToken->nType = JX9_TK_KEYWORD;
 25939  			pToken->pUserData = SX_INT_TO_PTR(nKeyword);
 25940  		}else{
 25941  			/* A simple identifier */
 25942  			pToken->nType = JX9_TK_ID;
 25943  		}
 25944  	}else{
 25945  		sxi32 c;
 25946  		/* Non-alpha stream */
 25947  		if( pStream->zText[0] == '#' || 
 25948  			( pStream->zText[0] == '/' &&  &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){
 25949  				pStream->zText++;
 25950  				/* Inline comments */
 25951  				while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){
 25952  					pStream->zText++;
 25953  				}
 25954  				/* Tell the upper-layer to ignore this token */ 
 25955  				return SXERR_CONTINUE;
 25956  		}else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){
 25957  			pStream->zText += 2;
 25958  			/* Block comment */
 25959  			while( pStream->zText < pStream->zEnd ){
 25960  				if( pStream->zText[0] == '*' ){
 25961  					if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/'  ){
 25962  						break;
 25963  					}
 25964  				}
 25965  				if( pStream->zText[0] == '\n' ){
 25966  					pStream->nLine++;
 25967  				}
 25968  				pStream->zText++;
 25969  			}
 25970  			pStream->zText += 2;
 25971  			/* Tell the upper-layer to ignore this token */
 25972  			return SXERR_CONTINUE;
 25973  		}else if( SyisDigit(pStream->zText[0]) ){
 25974  			pStream->zText++;
 25975  			/* Decimal digit stream */
 25976  			while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25977  				pStream->zText++;
 25978  			}
 25979  			/* Mark the token as integer until we encounter a real number */
 25980  			pToken->nType = JX9_TK_INTEGER;
 25981  			if( pStream->zText < pStream->zEnd ){
 25982  				c = pStream->zText[0];
 25983  				if( c == '.' ){
 25984  					/* Real number */
 25985  					pStream->zText++;
 25986  					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 25987  						pStream->zText++;
 25988  					}
 25989  					if( pStream->zText < pStream->zEnd ){
 25990  						c = pStream->zText[0];
 25991  						if( c=='e' || c=='E' ){
 25992  							pStream->zText++;
 25993  							if( pStream->zText < pStream->zEnd ){
 25994  								c = pStream->zText[0];
 25995  								if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
 25996  									pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
 25997  										pStream->zText++;
 25998  								}
 25999  								while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 26000  									pStream->zText++;
 26001  								}
 26002  							}
 26003  						}
 26004  					}
 26005  					pToken->nType = JX9_TK_REAL;
 26006  				}else if( c=='e' || c=='E' ){
 26007  					SXUNUSED(pUserData); /* Prevent compiler warning */
 26008  					SXUNUSED(pCtxData);
 26009  					pStream->zText++;
 26010  					if( pStream->zText < pStream->zEnd ){
 26011  						c = pStream->zText[0];
 26012  						if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
 26013  							pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
 26014  								pStream->zText++;
 26015  						}
 26016  						while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
 26017  							pStream->zText++;
 26018  						}
 26019  					}
 26020  					pToken->nType = JX9_TK_REAL;
 26021  				}else if( c == 'x' || c == 'X' ){
 26022  					/* Hex digit stream */
 26023  					pStream->zText++;
 26024  					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){
 26025  						pStream->zText++;
 26026  					}
 26027  				}else if(c  == 'b' || c == 'B' ){
 26028  					/* Binary digit stream */
 26029  					pStream->zText++;
 26030  					while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){
 26031  						pStream->zText++;
 26032  					}
 26033  				}
 26034  			}
 26035  			/* Record token length */
 26036  			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 26037  			return SXRET_OK;
 26038  		}
 26039  		c = pStream->zText[0];
 26040  		pStream->zText++; /* Advance the stream cursor */
 26041  		/* Assume we are dealing with an operator*/
 26042  		pToken->nType = JX9_TK_OP;
 26043  		switch(c){
 26044  		case '$': pToken->nType = JX9_TK_DOLLAR; break;
 26045  		case '{': pToken->nType = JX9_TK_OCB;   break; 
 26046  		case '}': pToken->nType = JX9_TK_CCB;    break;
 26047  		case '(': pToken->nType = JX9_TK_LPAREN; break; 
 26048  		case '[': pToken->nType |= JX9_TK_OSB;   break; /* Bitwise operation here, since the square bracket token '[' 
 26049  														 * is a potential operator [i.e: subscripting] */
 26050  		case ']': pToken->nType = JX9_TK_CSB;    break;
 26051  		case ')': {
 26052  			SySet *pTokSet = pStream->pSet;
 26053  			/* Assemble type cast operators [i.e: (int), (float), (bool)...] */ 
 26054  			if( pTokSet->nUsed >= 2 ){
 26055  				SyToken *pTmp;
 26056  				/* Peek the last recongnized token */
 26057  				pTmp = (SyToken *)SySetPeek(pTokSet);
 26058  				if( pTmp->nType & JX9_TK_KEYWORD ){
 26059  					sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData);
 26060  					if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){
 26061  						pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2);
 26062  						if( pTmp->nType & JX9_TK_LPAREN ){
 26063  							/* Merge the three tokens '(' 'TYPE' ')' into a single one */
 26064  							const char * zTypeCast = "(int)";
 26065  							if( nID & JX9_TKWRD_FLOAT ){
 26066  								zTypeCast = "(float)";
 26067  							}else if( nID & JX9_TKWRD_BOOL ){
 26068  								zTypeCast = "(bool)";
 26069  							}else if( nID & JX9_TKWRD_STRING ){
 26070  								zTypeCast = "(string)";
 26071  							}
 26072  							/* Reflect the change */
 26073  							pToken->nType = JX9_TK_OP;
 26074  							SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast));
 26075  							/* Save the instance associated with the type cast operator */
 26076  							pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0);
 26077  							/* Remove the two previous tokens */
 26078  							pTokSet->nUsed -= 2;
 26079  							return SXRET_OK;
 26080  						}
 26081  					}
 26082  				}
 26083  			}
 26084  			pToken->nType = JX9_TK_RPAREN;
 26085  			break;
 26086  				  }
 26087  		case '\'':{
 26088  			/* Single quoted string */
 26089  			pStr->zString++;
 26090  			while( pStream->zText < pStream->zEnd ){
 26091  				if( pStream->zText[0] == '\''  ){
 26092  					if( pStream->zText[-1] != '\\' ){
 26093  						break;
 26094  					}else{
 26095  						const unsigned char *zPtr = &pStream->zText[-2];
 26096  						sxi32 i = 1;
 26097  						while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
 26098  							zPtr--;
 26099  							i++;
 26100  						}
 26101  						if((i&1)==0){
 26102  							break;
 26103  						}
 26104  					}
 26105  				}
 26106  				if( pStream->zText[0] == '\n' ){
 26107  					pStream->nLine++;
 26108  				}
 26109  				pStream->zText++;
 26110  			}
 26111  			/* Record token length and type */
 26112  			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 26113  			pToken->nType = JX9_TK_SSTR;
 26114  			/* Jump the trailing single quote */
 26115  			pStream->zText++;
 26116  			return SXRET_OK;
 26117  				  }
 26118  		case '"':{
 26119  			sxi32 iNest;
 26120  			/* Double quoted string */
 26121  			pStr->zString++;
 26122  			while( pStream->zText < pStream->zEnd ){
 26123  				if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){
 26124  					iNest = 1;
 26125  					pStream->zText++;
 26126  					/* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */
 26127  					while(pStream->zText < pStream->zEnd ){
 26128  						if( pStream->zText[0] == '{' ){
 26129  							iNest++;
 26130  						}else if (pStream->zText[0] == '}' ){
 26131  							iNest--;
 26132  							if( iNest <= 0 ){
 26133  								pStream->zText++;
 26134  								break;
 26135  							}
 26136  						}else if( pStream->zText[0] == '\n' ){
 26137  							pStream->nLine++;
 26138  						}
 26139  						pStream->zText++;
 26140  					}
 26141  					if( pStream->zText >= pStream->zEnd ){
 26142  						break;
 26143  					}
 26144  				}
 26145  				if( pStream->zText[0] == '"' ){
 26146  					if( pStream->zText[-1] != '\\' ){
 26147  						break;
 26148  					}else{
 26149  						const unsigned char *zPtr = &pStream->zText[-2];
 26150  						sxi32 i = 1;
 26151  						while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
 26152  							zPtr--;
 26153  							i++;
 26154  						}
 26155  						if((i&1)==0){
 26156  							break;
 26157  						}
 26158  					}
 26159  				}
 26160  				if( pStream->zText[0] == '\n' ){
 26161  					pStream->nLine++;
 26162  				}
 26163  				pStream->zText++;
 26164  			}
 26165  			/* Record token length and type */
 26166  			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 26167  			pToken->nType = JX9_TK_DSTR;
 26168  			/* Jump the trailing quote */
 26169  			pStream->zText++;
 26170  			return SXRET_OK;
 26171  				  }
 26172  		case ':':
 26173  			pToken->nType = JX9_TK_COLON; /* Single colon */
 26174  			break;
 26175  		case ',': pToken->nType |= JX9_TK_COMMA;  break; /* Comma is also an operator */
 26176  		case ';': pToken->nType = JX9_TK_SEMI;   break;
 26177  			/* Handle combined operators [i.e: +=, ===, !=== ...] */
 26178  		case '=':
 26179  			pToken->nType |= JX9_TK_EQUAL;
 26180  			if( pStream->zText < pStream->zEnd ){
 26181  				if( pStream->zText[0] == '=' ){
 26182  					pToken->nType &= ~JX9_TK_EQUAL;
 26183  					/* Current operator: == */
 26184  					pStream->zText++;
 26185  					if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26186  						/* Current operator: === */
 26187  						pStream->zText++;
 26188  					}
 26189  				}
 26190  			}
 26191  			break;
 26192  		case '!':
 26193  			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26194  				/* Current operator: != */
 26195  				pStream->zText++;
 26196  				if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26197  					/* Current operator: !== */
 26198  					pStream->zText++;
 26199  				}
 26200  			}
 26201  			break;
 26202  		case '&':
 26203  			pToken->nType |= JX9_TK_AMPER;
 26204  			if( pStream->zText < pStream->zEnd ){
 26205  				if( pStream->zText[0] == '&' ){
 26206  					pToken->nType &= ~JX9_TK_AMPER;
 26207  					/* Current operator: && */
 26208  					pStream->zText++;
 26209  				}else if( pStream->zText[0] == '=' ){
 26210  					pToken->nType &= ~JX9_TK_AMPER;
 26211  					/* Current operator: &= */
 26212  					pStream->zText++;
 26213  				}
 26214  			}
 26215  		case '.':
 26216  			if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){
 26217  				/* Concatenation operator: '..' or '.='  */
 26218  				pStream->zText++;
 26219  			}
 26220  			break;
 26221  		case '|':
 26222  			if( pStream->zText < pStream->zEnd ){
 26223  				if( pStream->zText[0] == '|' ){
 26224  					/* Current operator: || */
 26225  					pStream->zText++;
 26226  				}else if( pStream->zText[0] == '=' ){
 26227  					/* Current operator: |= */
 26228  					pStream->zText++;
 26229  				}
 26230  			}
 26231  			break;
 26232  		case '+':
 26233  			if( pStream->zText < pStream->zEnd ){
 26234  				if( pStream->zText[0] == '+' ){
 26235  					/* Current operator: ++ */
 26236  					pStream->zText++;
 26237  				}else if( pStream->zText[0] == '=' ){
 26238  					/* Current operator: += */
 26239  					pStream->zText++;
 26240  				}
 26241  			}
 26242  			break;
 26243  		case '-':
 26244  			if( pStream->zText < pStream->zEnd ){
 26245  				if( pStream->zText[0] == '-' ){
 26246  					/* Current operator: -- */
 26247  					pStream->zText++;
 26248  				}else if( pStream->zText[0] == '=' ){
 26249  					/* Current operator: -= */
 26250  					pStream->zText++;
 26251  				}else if( pStream->zText[0] == '>' ){
 26252  					/* Current operator: -> */
 26253  					pStream->zText++;
 26254  				}
 26255  			}
 26256  			break;
 26257  		case '*':
 26258  			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26259  				/* Current operator: *= */
 26260  				pStream->zText++;
 26261  			}
 26262  			break;
 26263  		case '/':
 26264  			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26265  				/* Current operator: /= */
 26266  				pStream->zText++;
 26267  			}
 26268  			break;
 26269  		case '%':
 26270  			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26271  				/* Current operator: %= */
 26272  				pStream->zText++;
 26273  			}
 26274  			break;
 26275  		case '^':
 26276  			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26277  				/* Current operator: ^= */
 26278  				pStream->zText++;
 26279  			}
 26280  			break;
 26281  		case '<':
 26282  			if( pStream->zText < pStream->zEnd ){
 26283  				if( pStream->zText[0] == '<' ){
 26284  					/* Current operator: << */
 26285  					pStream->zText++;
 26286  					if( pStream->zText < pStream->zEnd ){
 26287  						if( pStream->zText[0] == '=' ){
 26288  							/* Current operator: <<= */
 26289  							pStream->zText++;
 26290  						}else if( pStream->zText[0] == '<' ){
 26291  							/* Current Token: <<<  */
 26292  							pStream->zText++;
 26293  							/* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */
 26294  							rc = LexExtractNowdoc(&(*pStream), &(*pToken));
 26295  							if( rc == SXRET_OK ){
 26296  								/* Here/Now doc successfuly extracted */
 26297  								return SXRET_OK;
 26298  							}
 26299  						}
 26300  					}
 26301  				}else if( pStream->zText[0] == '>' ){
 26302  					/* Current operator: <> */
 26303  					pStream->zText++;
 26304  				}else if( pStream->zText[0] == '=' ){
 26305  					/* Current operator: <= */
 26306  					pStream->zText++;
 26307  				}
 26308  			}
 26309  			break;
 26310  		case '>':
 26311  			if( pStream->zText < pStream->zEnd ){
 26312  				if( pStream->zText[0] == '>' ){
 26313  					/* Current operator: >> */
 26314  					pStream->zText++;
 26315  					if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
 26316  						/* Current operator: >>= */
 26317  						pStream->zText++;
 26318  					}
 26319  				}else if( pStream->zText[0] == '=' ){
 26320  					/* Current operator: >= */
 26321  					pStream->zText++;
 26322  				}
 26323  			}
 26324  			break;
 26325  		default:
 26326  			break;
 26327  		}
 26328  		if( pStr->nByte <= 0 ){
 26329  			/* Record token length */
 26330  			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
 26331  		}
 26332  		if( pToken->nType & JX9_TK_OP ){
 26333  			const jx9_expr_op *pOp;
 26334  			/* Check if the extracted token is an operator */
 26335  			pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet));
 26336  			if( pOp == 0 ){
 26337  				/* Not an operator */
 26338  				pToken->nType &= ~JX9_TK_OP;
 26339  				if( pToken->nType <= 0 ){
 26340  					pToken->nType = JX9_TK_OTHER;
 26341  				}
 26342  			}else{
 26343  				/* Save the instance associated with this operator for later processing */
 26344  				pToken->pUserData = (void *)pOp;
 26345  			}
 26346  		}
 26347  	}
 26348  	/* Tell the upper-layer to save the extracted token for later processing */
 26349  	return SXRET_OK;
 26350  }
 26351  /***** This file contains automatically generated code ******
 26352  **
 26353  ** The code in this file has been automatically generated by
 26354  **
 26355  **     $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 <chm@symisc.net> $
 26356  **
 26357  ** The code in this file implements a function that determines whether
 26358  ** or not a given identifier is really a JX9 keyword.  The same thing
 26359  ** might be implemented more directly using a hand-written hash table.
 26360  ** But by using this automatically generated code, the size of the code
 26361  ** is substantially reduced.  This is important for embedded applications
 26362  ** on platforms with limited memory.
 26363  */
 26364  /* Hash score: 35 */
 26365  static sxu32 keywordCode(const char *z, int n)
 26366  {
 26367    /* zText[] encodes 188 bytes of keywords in 128 bytes */
 26368    /*   printegereturnconstaticaselseifloatincludefaultDIEXITcontinue      */
 26369    /*   diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch       */
 26370    /*   uplink                                                             */
 26371    static const char zText[127] = {
 26372      'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s',
 26373      't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i',
 26374      'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c',
 26375      'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P',
 26376      'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o',
 26377      'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r',
 26378      't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n',
 26379      'k',
 26380    };
 26381    static const unsigned char aHash[59] = {
 26382         0,   0,   0,   0,  15,   0,  30,   0,   0,   2,  19,  18,   0,
 26383         0,  10,   3,  12,   0,  28,  29,  23,   0,  13,  22,   0,   0,
 26384        14,  24,  25,  31,  11,   0,   0,   0,   0,   1,   5,   0,   0,
 26385        20,   0,  27,   9,   0,   0,   0,   8,   0,   0,  26,   6,   0,
 26386         0,  17,   0,   0,   0,   0,   0,
 26387    };
 26388    static const unsigned char aNext[31] = {
 26389         0,   0,   0,   0,   0,   0,   0,   0,   0,   4,   0,   0,   0,
 26390         0,   0,   0,   0,   0,   0,   0,   0,   0,  16,   0,  21,   7,
 26391         0,   0,   0,   0,   0,
 26392    };
 26393    static const unsigned char aLen[31] = {
 26394         5,   7,   3,   6,   5,   6,   4,   2,   6,   4,   2,   5,   7,
 26395         7,   3,   4,   8,   3,   5,   2,   5,   4,   7,   5,   3,   7,
 26396         8,   6,   6,   6,   6,
 26397    };
 26398    static const sxu16 aOffset[31] = {
 26399         0,   2,   2,   8,  14,  17,  22,  23,  25,  25,  29,  30,  35,
 26400        40,  47,  49,  53,  61,  64,  69,  71,  76,  76,  83,  88,  88,
 26401        95, 103, 109, 115, 121,
 26402    };
 26403    static const sxu32 aCode[31] = {
 26404      JX9_TKWRD_PRINT,   JX9_TKWRD_INT,      JX9_TKWRD_INT,     JX9_TKWRD_RETURN,   JX9_TKWRD_CONST, 
 26405      JX9_TKWRD_STATIC,  JX9_TKWRD_CASE,     JX9_TKWRD_AS,      JX9_TKWRD_ELIF,     JX9_TKWRD_ELSE,
 26406      JX9_TKWRD_IF,      JX9_TKWRD_FLOAT,    JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT,  JX9_TKWRD_DIE, 
 26407      JX9_TKWRD_EXIT,    JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE,     JX9_TKWRD_WHILE,    JX9_TKWRD_AS,  
 26408      JX9_TKWRD_PRINT,   JX9_TKWRD_BOOL,     JX9_TKWRD_BOOL,    JX9_TKWRD_BREAK,    JX9_TKWRD_FOR, 
 26409      JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT,  JX9_TKWRD_STRING,  JX9_TKWRD_SWITCH,  
 26410      JX9_TKWRD_UPLINK,  
 26411    };
 26412    int h, i;
 26413    if( n<2 ) return JX9_TK_ID;
 26414    h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59;
 26415    for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
 26416      if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){
 26417         /* JX9_TKWRD_PRINT */
 26418         /* JX9_TKWRD_INT */
 26419         /* JX9_TKWRD_INT */
 26420         /* JX9_TKWRD_RETURN */
 26421         /* JX9_TKWRD_CONST */
 26422         /* JX9_TKWRD_STATIC */
 26423         /* JX9_TKWRD_CASE */
 26424         /* JX9_TKWRD_AS */
 26425         /* JX9_TKWRD_ELIF */
 26426         /* JX9_TKWRD_ELSE */
 26427         /* JX9_TKWRD_IF */
 26428         /* JX9_TKWRD_FLOAT */
 26429         /* JX9_TKWRD_INCLUDE */
 26430         /* JX9_TKWRD_DEFAULT */
 26431         /* JX9_TKWRD_DIE */
 26432         /* JX9_TKWRD_EXIT */
 26433         /* JX9_TKWRD_CONTINUE */
 26434         /* JX9_TKWRD_DIE */
 26435         /* JX9_TKWRD_WHILE */
 26436         /* JX9_TKWRD_AS */
 26437         /* JX9_TKWRD_PRINT */
 26438         /* JX9_TKWRD_BOOL */
 26439         /* JX9_TKWRD_BOOL */
 26440         /* JX9_TKWRD_BREAK */
 26441         /* JX9_TKWRD_FOR */
 26442         /* JX9_TKWRD_FOREACH */
 26443         /* JX9_TKWRD_FUNCTION */
 26444         /* JX9_TKWRD_IMPORT */
 26445         /* JX9_TKWRD_STRING */
 26446         /* JX9_TKWRD_SWITCH */
 26447         /* JX9_TKWRD_UPLINK */
 26448        return aCode[i];
 26449      }
 26450    }
 26451    return JX9_TK_ID;
 26452  }
 26453  /*
 26454   * Extract a heredoc/nowdoc text from a raw JX9 input.
 26455   * According to the JX9 language reference manual:
 26456   *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
 26457   *  is provided, then a newline. The string itself follows, and then the same identifier again
 26458   *  to close the quotation.
 26459   *  The closing identifier must begin in the first column of the line. Also, the identifier must 
 26460   *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric 
 26461   *  characters and underscores, and must start with a non-digit character or underscore. 
 26462   *  Heredoc text behaves just like a double-quoted string, without the double quotes.
 26463   *  This means that quotes in a heredoc do not need to be escaped, but the escape codes listed
 26464   *  above can still be used. Variables are expanded, but the same care must be taken when expressing
 26465   *  complex variables inside a heredoc as with strings. 
 26466   *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
 26467   *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
 26468   *  The construct is ideal for embedding JX9 code or other large blocks of text without the need
 26469   *  for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that
 26470   *  it declares a block of text which is not for parsing.
 26471   *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows
 26472   *  is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc
 26473   *  identifiers, especially those regarding the appearance of the closing identifier.
 26474   */
 26475  static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken)
 26476  {
 26477  	const unsigned char *zIn  = pStream->zText;
 26478  	const unsigned char *zEnd = pStream->zEnd;
 26479  	const unsigned char *zPtr;
 26480  	SyString sDelim;
 26481  	SyString sStr;
 26482  	/* Jump leading white spaces */
 26483  	while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
 26484  		zIn++;
 26485  	}
 26486  	if( zIn >= zEnd ){
 26487  		/* A simple symbol, return immediately */
 26488  		return SXERR_CONTINUE;
 26489  	}
 26490  	if( zIn[0] == '\'' || zIn[0] == '"' ){
 26491  		zIn++;
 26492  	}
 26493  	if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){
 26494  		/* Invalid delimiter, return immediately */
 26495  		return SXERR_CONTINUE;
 26496  	}
 26497  	/* Isolate the identifier */
 26498  	sDelim.zString = (const char *)zIn;
 26499  	for(;;){
 26500  		zPtr = zIn;
 26501  		/* Skip alphanumeric stream */
 26502  		while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){
 26503  			zPtr++;
 26504  		}
 26505  		if( zPtr < zEnd && zPtr[0] >= 0xc0 ){
 26506  			zPtr++;
 26507  			/* UTF-8 stream */
 26508  			while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){
 26509  				zPtr++;
 26510  			}
 26511  		}
 26512  		if( zPtr == zIn ){
 26513  			/* Not an UTF-8 or alphanumeric stream */
 26514  			break;
 26515  		}
 26516  		/* Synchronize pointers */
 26517  		zIn = zPtr;
 26518  	}
 26519  	/* Get the identifier length */
 26520  	sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString);
 26521  	if( zIn[0] == '"' || zIn[0] == '\'' ){
 26522  		/* Jump the trailing single quote */
 26523  		zIn++;
 26524  	}
 26525  	/* Jump trailing white spaces */
 26526  	while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
 26527  		zIn++;
 26528  	}
 26529  	if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){
 26530  		/* Invalid syntax */
 26531  		return SXERR_CONTINUE;
 26532  	}
 26533  	pStream->nLine++; /* Increment line counter */
 26534  	zIn++;
 26535  	/* Isolate the delimited string */
 26536  	sStr.zString = (const char *)zIn;
 26537  	/* Go and found the closing delimiter */
 26538  	for(;;){
 26539  		/* Synchronize with the next line */
 26540  		while( zIn < zEnd && zIn[0] != '\n' ){
 26541  			zIn++;
 26542  		}
 26543  		if( zIn >= zEnd ){
 26544  			/* End of the input reached, break immediately */
 26545  			pStream->zText = pStream->zEnd;
 26546  			break;
 26547  		}
 26548  		pStream->nLine++; /* Increment line counter */
 26549  		zIn++;
 26550  		if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){
 26551  			zPtr = &zIn[sDelim.nByte];
 26552  			while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
 26553  				zPtr++;
 26554  			}
 26555  			if( zPtr >= zEnd ){
 26556  				/* End of input */
 26557  				pStream->zText = zPtr;
 26558  				break;
 26559  			}
 26560  			if( zPtr[0] == ';' ){
 26561  				const unsigned char *zCur = zPtr;
 26562  				zPtr++;
 26563  				while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
 26564  					zPtr++;
 26565  				}
 26566  				if( zPtr >= zEnd || zPtr[0] == '\n' ){
 26567  					/* Closing delimiter found, break immediately */
 26568  					pStream->zText = zCur; /* Keep the semi-colon */
 26569  					break;
 26570  				}
 26571  			}else if( zPtr[0] == '\n' ){
 26572  				/* Closing delimiter found, break immediately */
 26573  				pStream->zText = zPtr; /* Synchronize with the stream cursor */
 26574  				break;
 26575  			}
 26576  			/* Synchronize pointers and continue searching */
 26577  			zIn = zPtr;
 26578  		}
 26579  	} /* For(;;) */
 26580  	/* Get the delimited string length */
 26581  	sStr.nByte = (sxu32)((const char *)zIn-sStr.zString);
 26582  	/* Record token type and length */
 26583  	pToken->nType = JX9_TK_NOWDOC;
 26584  	SyStringDupPtr(&pToken->sData, &sStr);
 26585  	/* Remove trailing white spaces */
 26586  	SyStringRightTrim(&pToken->sData);
 26587  	/* All done */
 26588  	return SXRET_OK;
 26589  }
 26590  /*
 26591   * Tokenize a raw jx9 input.
 26592   * This is the public tokenizer called by most code generator routines. 
 26593   */
 26594  JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut)
 26595  {
 26596  	SyLex sLexer;
 26597  	sxi32 rc;
 26598  	/* Initialize the lexer */
 26599  	rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0);
 26600  	if( rc != SXRET_OK ){
 26601  		return rc;
 26602  	}
 26603  	/* Tokenize input */
 26604  	rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0);
 26605  	/* Release the lexer */
 26606  	SyLexRelease(&sLexer);
 26607  	/* Tokenization result */
 26608  	return rc;
 26609  }
 26610  
 26611  /*
 26612   * ----------------------------------------------------------
 26613   * File: jx9_lib.c
 26614   * MD5: a684fb6677b1ab0110d03536f1280c50
 26615   * ----------------------------------------------------------
 26616   */
 26617  /*
 26618   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 26619   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 26620   * Version 1.7.2
 26621   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 26622   * please contact Symisc Systems via:
 26623   *       legal@symisc.net
 26624   *       licensing@symisc.net
 26625   *       contact@symisc.net
 26626   * or visit:
 26627   *      http://jx9.symisc.net/
 26628   */
 26629   /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable <chm@symisc.net> $ */
 26630  /*
 26631   * Symisc Run-Time API: A modern thread safe replacement of the standard libc
 26632   * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/
 26633   *
 26634   * The Symisc Run-Time API is an independent project developed by symisc systems
 26635   * internally as a secure replacement of the standard libc.
 26636   * The library is re-entrant, thread-safe and platform independent.
 26637   */
 26638  #ifndef JX9_AMALGAMATION
 26639  #include "jx9Int.h"
 26640  #endif
 26641  #if defined(__WINNT__)
 26642  #include <Windows.h>
 26643  #else
 26644  #include <stdlib.h>
 26645  #endif
 26646  #if defined(JX9_ENABLE_THREADS)
 26647  /* SyRunTimeApi: sxmutex.c */
 26648  #if defined(__WINNT__)
 26649  struct SyMutex
 26650  {
 26651  	CRITICAL_SECTION sMutex;
 26652  	sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */
 26653  };
 26654  /* Preallocated static mutex */
 26655  static SyMutex aStaticMutexes[] = {
 26656  		{{0}, SXMUTEX_TYPE_STATIC_1}, 
 26657  		{{0}, SXMUTEX_TYPE_STATIC_2}, 
 26658  		{{0}, SXMUTEX_TYPE_STATIC_3}, 
 26659  		{{0}, SXMUTEX_TYPE_STATIC_4}, 
 26660  		{{0}, SXMUTEX_TYPE_STATIC_5}, 
 26661  		{{0}, SXMUTEX_TYPE_STATIC_6}
 26662  };
 26663  static BOOL winMutexInit = FALSE;
 26664  static LONG winMutexLock = 0;
 26665  
 26666  static sxi32 WinMutexGlobaInit(void)
 26667  {
 26668  	LONG rc;
 26669  	rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
 26670  	if ( rc == 0 ){
 26671  		sxu32 n;
 26672  		for( n = 0 ; n  < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
 26673  			InitializeCriticalSection(&aStaticMutexes[n].sMutex);
 26674  		}
 26675  		winMutexInit = TRUE;
 26676  	}else{
 26677  		/* Someone else is doing this for us */
 26678  		while( winMutexInit == FALSE ){
 26679  			Sleep(1);
 26680  		}
 26681  	}
 26682  	return SXRET_OK;
 26683  }
 26684  static void WinMutexGlobalRelease(void)
 26685  {
 26686  	LONG rc;
 26687  	rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
 26688  	if( rc == 1 ){
 26689  		/* The first to decrement to zero does the actual global release */
 26690  		if( winMutexInit == TRUE ){
 26691  			sxu32 n;
 26692  			for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
 26693  				DeleteCriticalSection(&aStaticMutexes[n].sMutex);
 26694  			}
 26695  			winMutexInit = FALSE;
 26696  		}
 26697  	}
 26698  }
 26699  static SyMutex * WinMutexNew(int nType)
 26700  {
 26701  	SyMutex *pMutex = 0;
 26702  	if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
 26703  		/* Allocate a new mutex */
 26704  		pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
 26705  		if( pMutex == 0 ){
 26706  			return 0;
 26707  		}
 26708  		InitializeCriticalSection(&pMutex->sMutex);
 26709  	}else{
 26710  		/* Use a pre-allocated static mutex */
 26711  		if( nType > SXMUTEX_TYPE_STATIC_6 ){
 26712  			nType = SXMUTEX_TYPE_STATIC_6;
 26713  		}
 26714  		pMutex = &aStaticMutexes[nType - 3];
 26715  	}
 26716  	pMutex->nType = nType;
 26717  	return pMutex;
 26718  }
 26719  static void WinMutexRelease(SyMutex *pMutex)
 26720  {
 26721  	if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
 26722  		DeleteCriticalSection(&pMutex->sMutex);
 26723  		HeapFree(GetProcessHeap(), 0, pMutex);
 26724  	}
 26725  }
 26726  static void WinMutexEnter(SyMutex *pMutex)
 26727  {
 26728  	EnterCriticalSection(&pMutex->sMutex);
 26729  }
 26730  static sxi32 WinMutexTryEnter(SyMutex *pMutex)
 26731  {
 26732  #ifdef _WIN32_WINNT
 26733  	BOOL rc;
 26734  	/* Only WindowsNT platforms */
 26735  	rc = TryEnterCriticalSection(&pMutex->sMutex);
 26736  	if( rc ){
 26737  		return SXRET_OK;
 26738  	}else{
 26739  		return SXERR_BUSY;
 26740  	}
 26741  #else
 26742  	return SXERR_NOTIMPLEMENTED;
 26743  #endif
 26744  }
 26745  static void WinMutexLeave(SyMutex *pMutex)
 26746  {
 26747  	LeaveCriticalSection(&pMutex->sMutex);
 26748  }
 26749  /* Export Windows mutex interfaces */
 26750  static const SyMutexMethods sWinMutexMethods = {
 26751  	WinMutexGlobaInit,  /* xGlobalInit() */
 26752  	WinMutexGlobalRelease, /* xGlobalRelease() */
 26753  	WinMutexNew,     /* xNew() */
 26754  	WinMutexRelease, /* xRelease() */
 26755  	WinMutexEnter,   /* xEnter() */
 26756  	WinMutexTryEnter, /* xTryEnter() */
 26757  	WinMutexLeave     /* xLeave() */
 26758  };
 26759  JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
 26760  {
 26761  	return &sWinMutexMethods;
 26762  }
 26763  #elif defined(__UNIXES__)
 26764  #include <pthread.h>
 26765  struct SyMutex
 26766  {
 26767  	pthread_mutex_t sMutex;
 26768  	sxu32 nType;
 26769  };
 26770  static SyMutex * UnixMutexNew(int nType)
 26771  {
 26772  	static SyMutex aStaticMutexes[] = {
 26773  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, 
 26774  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, 
 26775  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, 
 26776  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, 
 26777  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, 
 26778  		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
 26779  	};
 26780  	SyMutex *pMutex;
 26781  	
 26782  	if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
 26783  		pthread_mutexattr_t sRecursiveAttr;
 26784    		/* Allocate a new mutex */
 26785    		pMutex = (SyMutex *)malloc(sizeof(SyMutex));
 26786    		if( pMutex == 0 ){
 26787    			return 0;
 26788    		}
 26789    		if( nType == SXMUTEX_TYPE_RECURSIVE ){
 26790    			pthread_mutexattr_init(&sRecursiveAttr);
 26791    			pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
 26792    		}
 26793    		pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 );
 26794  		if(	nType == SXMUTEX_TYPE_RECURSIVE ){
 26795     			pthread_mutexattr_destroy(&sRecursiveAttr);
 26796  		}
 26797  	}else{
 26798  		/* Use a pre-allocated static mutex */
 26799  		if( nType > SXMUTEX_TYPE_STATIC_6 ){
 26800  			nType = SXMUTEX_TYPE_STATIC_6;
 26801  		}
 26802  		pMutex = &aStaticMutexes[nType - 3];
 26803  	}
 26804    pMutex->nType = nType;
 26805    
 26806    return pMutex;
 26807  }
 26808  static void UnixMutexRelease(SyMutex *pMutex)
 26809  {
 26810  	if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
 26811  		pthread_mutex_destroy(&pMutex->sMutex);
 26812  		free(pMutex);
 26813  	}
 26814  }
 26815  static void UnixMutexEnter(SyMutex *pMutex)
 26816  {
 26817  	pthread_mutex_lock(&pMutex->sMutex);
 26818  }
 26819  static void UnixMutexLeave(SyMutex *pMutex)
 26820  {
 26821  	pthread_mutex_unlock(&pMutex->sMutex);
 26822  }
 26823  /* Export pthread mutex interfaces */
 26824  static const SyMutexMethods sPthreadMutexMethods = {
 26825  	0, /* xGlobalInit() */
 26826  	0, /* xGlobalRelease() */
 26827  	UnixMutexNew,      /* xNew() */
 26828  	UnixMutexRelease,  /* xRelease() */
 26829  	UnixMutexEnter,    /* xEnter() */
 26830  	0,                 /* xTryEnter() */
 26831  	UnixMutexLeave     /* xLeave() */
 26832  };
 26833  JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
 26834  {
 26835  	return &sPthreadMutexMethods;
 26836  }
 26837  #else
 26838  /* Host application must register their own mutex subsystem if the target
 26839   * platform is not an UNIX-like or windows systems.
 26840   */
 26841  struct SyMutex
 26842  {
 26843  	sxu32 nType;
 26844  };
 26845  static SyMutex * DummyMutexNew(int nType)
 26846  {
 26847  	static SyMutex sMutex;
 26848  	SXUNUSED(nType);
 26849  	return &sMutex;
 26850  }
 26851  static void DummyMutexRelease(SyMutex *pMutex)
 26852  {
 26853  	SXUNUSED(pMutex);
 26854  }
 26855  static void DummyMutexEnter(SyMutex *pMutex)
 26856  {
 26857  	SXUNUSED(pMutex);
 26858  }
 26859  static void DummyMutexLeave(SyMutex *pMutex)
 26860  {
 26861  	SXUNUSED(pMutex);
 26862  }
 26863  /* Export the dummy mutex interfaces */
 26864  static const SyMutexMethods sDummyMutexMethods = {
 26865  	0, /* xGlobalInit() */
 26866  	0, /* xGlobalRelease() */
 26867  	DummyMutexNew,      /* xNew() */
 26868  	DummyMutexRelease,  /* xRelease() */
 26869  	DummyMutexEnter,    /* xEnter() */
 26870  	0,                  /* xTryEnter() */
 26871  	DummyMutexLeave     /* xLeave() */
 26872  };
 26873  JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
 26874  {
 26875  	return &sDummyMutexMethods;
 26876  }
 26877  #endif /* __WINNT__ */
 26878  #endif /* JX9_ENABLE_THREADS */
 26879  static void * SyOSHeapAlloc(sxu32 nByte)
 26880  {
 26881  	void *pNew;
 26882  #if defined(__WINNT__)
 26883  	pNew = HeapAlloc(GetProcessHeap(), 0, nByte);
 26884  #else
 26885  	pNew = malloc((size_t)nByte);
 26886  #endif
 26887  	return pNew;
 26888  }
 26889  static void * SyOSHeapRealloc(void *pOld, sxu32 nByte)
 26890  {
 26891  	void *pNew;
 26892  #if defined(__WINNT__)
 26893  	pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte);
 26894  #else
 26895  	pNew = realloc(pOld, (size_t)nByte);
 26896  #endif
 26897  	return pNew;	
 26898  }
 26899  static void SyOSHeapFree(void *pPtr)
 26900  {
 26901  #if defined(__WINNT__)
 26902  	HeapFree(GetProcessHeap(), 0, pPtr);
 26903  #else
 26904  	free(pPtr);
 26905  #endif
 26906  }
 26907  /* SyRunTimeApi:sxstr.c */
 26908  JX9_PRIVATE sxu32 SyStrlen(const char *zSrc)
 26909  {
 26910  	register const char *zIn = zSrc;
 26911  #if defined(UNTRUST)
 26912  	if( zIn == 0 ){
 26913  		return 0;
 26914  	}
 26915  #endif
 26916  	for(;;){
 26917  		if( !zIn[0] ){ break; } zIn++;
 26918  		if( !zIn[0] ){ break; } zIn++;
 26919  		if( !zIn[0] ){ break; } zIn++;
 26920  		if( !zIn[0] ){ break; } zIn++;	
 26921  	}
 26922  	return (sxu32)(zIn - zSrc);
 26923  }
 26924  JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
 26925  {
 26926  	const char *zIn = zStr;
 26927  	const char *zEnd;
 26928  	
 26929  	zEnd = &zIn[nLen];
 26930  	for(;;){
 26931  		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
 26932  		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
 26933  		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
 26934  		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
 26935  	}
 26936  	return SXERR_NOTFOUND;
 26937  }
 26938  #ifndef JX9_DISABLE_BUILTIN_FUNC
 26939  JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
 26940  {
 26941  	const char *zIn = zStr;
 26942  	const char *zEnd;
 26943  	
 26944  	zEnd = &zIn[nLen - 1];
 26945  	for( ;; ){
 26946  		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
 26947  		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
 26948  		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
 26949  		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
 26950  	}
 26951  	return SXERR_NOTFOUND; 
 26952  }
 26953  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 26954  JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos)
 26955  {
 26956  	const char *zIn = zSrc;
 26957  	const char *zPtr;
 26958  	const char *zEnd;
 26959  	sxi32 c;
 26960  	zEnd = &zSrc[nLen];
 26961  	for(;;){
 26962  		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++;
 26963  		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++;
 26964  		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++;
 26965  		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++;
 26966  	}	
 26967  	return SXERR_NOTFOUND; 
 26968  }
 26969  #ifndef JX9_DISABLE_BUILTIN_FUNC
 26970  JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen)
 26971  {
 26972  	const unsigned char *zP = (const unsigned char *)zLeft;
 26973  	const unsigned char *zQ = (const unsigned char *)zRight;
 26974  
 26975  	if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ)  ){
 26976  			return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1;
 26977  	}
 26978  	if( nLen <= 0 ){
 26979  		return 0;
 26980  	}
 26981  	for(;;){
 26982  		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
 26983  		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
 26984  		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
 26985  		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
 26986  	}
 26987  	return (sxi32)(zP[0] - zQ[0]);
 26988  }	
 26989  #endif
 26990  JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen)
 26991  {
 26992    	register unsigned char *p = (unsigned char *)zLeft;
 26993  	register unsigned char *q = (unsigned char *)zRight;
 26994  	
 26995  	if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){
 26996  		return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1;
 26997  	}
 26998  	for(;;){
 26999  		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
 27000  		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
 27001  		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
 27002  		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
 27003  		
 27004  	}
 27005  	return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0]));
 27006  }
 27007  JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen)
 27008  {
 27009  	unsigned char *zBuf = (unsigned char *)zDest;
 27010  	unsigned char *zIn = (unsigned char *)zSrc;
 27011  	unsigned char *zEnd;
 27012  #if defined(UNTRUST)
 27013  	if( zSrc == (const char *)zDest ){
 27014  			return 0;
 27015  	}
 27016  #endif
 27017  	if( nLen <= 0 ){
 27018  		nLen = SyStrlen(zSrc);
 27019  	}
 27020  	zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */
 27021  	for(;;){
 27022  		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
 27023  		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
 27024  		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
 27025  		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
 27026  	}
 27027  	zBuf[0] = 0;
 27028  	return (sxu32)(zBuf-(unsigned char *)zDest);
 27029  }
 27030  /* SyRunTimeApi:sxmem.c */
 27031  JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize)
 27032  {
 27033  	register unsigned char *zSrc = (unsigned char *)pSrc;
 27034  	unsigned char *zEnd;
 27035  #if defined(UNTRUST)
 27036  	if( zSrc == 0 || nSize <= 0 ){
 27037  		return ;
 27038  	}
 27039  #endif
 27040  	zEnd = &zSrc[nSize];
 27041  	for(;;){
 27042  		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
 27043  		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
 27044  		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
 27045  		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
 27046  	}
 27047  }
 27048  JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize)
 27049  {
 27050  	sxi32 rc;
 27051  	if( nSize <= 0 ){
 27052  		return 0;
 27053  	}
 27054  	if( pB1 == 0 || pB2 == 0 ){
 27055  		return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1);
 27056  	}
 27057  	SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc);
 27058  	return rc;
 27059  }
 27060  JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen)
 27061  {
 27062  	if( pSrc == 0 || pDest == 0 ){
 27063  		return 0;
 27064  	}
 27065  	if( pSrc == (const void *)pDest ){
 27066  		return nLen;
 27067  	}
 27068  	SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen);
 27069  	return nLen;
 27070  }
 27071  static void * MemOSAlloc(sxu32 nBytes)
 27072  {
 27073  	sxu32 *pChunk;
 27074  	pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32));
 27075  	if( pChunk == 0 ){
 27076  		return 0;
 27077  	}
 27078  	pChunk[0] = nBytes;
 27079  	return (void *)&pChunk[1];
 27080  }
 27081  static void * MemOSRealloc(void *pOld, sxu32 nBytes)
 27082  {
 27083  	sxu32 *pOldChunk;
 27084  	sxu32 *pChunk;
 27085  	pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32));
 27086  	if( pOldChunk[0] >= nBytes ){
 27087  		return pOld;
 27088  	}
 27089  	pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32));
 27090  	if( pChunk == 0 ){
 27091  		return 0;
 27092  	}
 27093  	pChunk[0] = nBytes;
 27094  	return (void *)&pChunk[1];
 27095  }
 27096  static void MemOSFree(void *pBlock)
 27097  {
 27098  	void *pChunk;
 27099  	pChunk = (void *)(((char *)pBlock)-sizeof(sxu32));
 27100  	SyOSHeapFree(pChunk);
 27101  }
 27102  static sxu32 MemOSChunkSize(void *pBlock)
 27103  {
 27104  	sxu32 *pChunk;
 27105  	pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32));
 27106  	return pChunk[0];
 27107  }
 27108  /* Export OS allocation methods */
 27109  static const SyMemMethods sOSAllocMethods = {
 27110  	MemOSAlloc, 
 27111  	MemOSRealloc, 
 27112  	MemOSFree, 
 27113  	MemOSChunkSize, 
 27114  	0, 
 27115  	0, 
 27116  	0
 27117  };
 27118  static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
 27119  {
 27120  	SyMemBlock *pBlock;
 27121  	sxi32 nRetry = 0;
 27122  
 27123  	/* Append an extra block so we can tracks allocated chunks and avoid memory
 27124  	 * leaks.
 27125  	 */
 27126  	nByte += sizeof(SyMemBlock);
 27127  	for(;;){
 27128  		pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte);
 27129  		if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY 
 27130  			|| SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
 27131  				break;
 27132  		}
 27133  		nRetry++;
 27134  	}
 27135  	if( pBlock  == 0 ){
 27136  		return 0;
 27137  	}
 27138  	pBlock->pNext = pBlock->pPrev = 0;
 27139  	/* Link to the list of already tracked blocks */
 27140  	MACRO_LD_PUSH(pBackend->pBlocks, pBlock);
 27141  #if defined(UNTRUST)
 27142  	pBlock->nGuard = SXMEM_BACKEND_MAGIC;
 27143  #endif
 27144  	pBackend->nBlock++;
 27145  	return (void *)&pBlock[1];
 27146  }
 27147  JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
 27148  {
 27149  	void *pChunk;
 27150  #if defined(UNTRUST)
 27151  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27152  		return 0;
 27153  	}
 27154  #endif
 27155  	if( pBackend->pMutexMethods ){
 27156  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27157  	}
 27158  	pChunk = MemBackendAlloc(&(*pBackend), nByte);
 27159  	if( pBackend->pMutexMethods ){
 27160  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27161  	}
 27162  	return pChunk;
 27163  }
 27164  static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
 27165  {
 27166  	SyMemBlock *pBlock, *pNew, *pPrev, *pNext;
 27167  	sxu32 nRetry = 0;
 27168  
 27169  	if( pOld == 0 ){
 27170  		return MemBackendAlloc(&(*pBackend), nByte);
 27171  	}
 27172  	pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock));
 27173  #if defined(UNTRUST)
 27174  	if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
 27175  		return 0;
 27176  	}
 27177  #endif
 27178  	nByte += sizeof(SyMemBlock);
 27179  	pPrev = pBlock->pPrev;
 27180  	pNext = pBlock->pNext;
 27181  	for(;;){
 27182  		pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte);
 27183  		if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY ||
 27184  			SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
 27185  				break;
 27186  		}
 27187  		nRetry++;
 27188  	}
 27189  	if( pNew == 0 ){
 27190  		return 0;
 27191  	}
 27192  	if( pNew != pBlock ){
 27193  		if( pPrev == 0 ){
 27194  			pBackend->pBlocks = pNew;
 27195  		}else{
 27196  			pPrev->pNext = pNew;
 27197  		}
 27198  		if( pNext ){
 27199  			pNext->pPrev = pNew;
 27200  		}
 27201  #if defined(UNTRUST)
 27202  		pNew->nGuard = SXMEM_BACKEND_MAGIC;
 27203  #endif
 27204  	}
 27205  	return (void *)&pNew[1];
 27206  }
 27207  JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
 27208  {
 27209  	void *pChunk;
 27210  #if defined(UNTRUST)
 27211  	if( SXMEM_BACKEND_CORRUPT(pBackend)  ){
 27212  		return 0;
 27213  	}
 27214  #endif
 27215  	if( pBackend->pMutexMethods ){
 27216  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27217  	}
 27218  	pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte);
 27219  	if( pBackend->pMutexMethods ){
 27220  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27221  	}
 27222  	return pChunk;
 27223  }
 27224  static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk)
 27225  {
 27226  	SyMemBlock *pBlock;
 27227  	pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock));
 27228  #if defined(UNTRUST)
 27229  	if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
 27230  		return SXERR_CORRUPT;
 27231  	}
 27232  #endif
 27233  	/* Unlink from the list of active blocks */
 27234  	if( pBackend->nBlock > 0 ){
 27235  		/* Release the block */
 27236  #if defined(UNTRUST)
 27237  		/* Mark as stale block */
 27238  		pBlock->nGuard = 0x635B;
 27239  #endif
 27240  		MACRO_LD_REMOVE(pBackend->pBlocks, pBlock);
 27241  		pBackend->nBlock--;
 27242  		pBackend->pMethods->xFree(pBlock);
 27243  	}
 27244  	return SXRET_OK;
 27245  }
 27246  JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk)
 27247  {
 27248  	sxi32 rc;
 27249  #if defined(UNTRUST)
 27250  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27251  		return SXERR_CORRUPT;
 27252  	}
 27253  #endif
 27254  	if( pChunk == 0 ){
 27255  		return SXRET_OK;
 27256  	}
 27257  	if( pBackend->pMutexMethods ){
 27258  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27259  	}
 27260  	rc = MemBackendFree(&(*pBackend), pChunk);
 27261  	if( pBackend->pMutexMethods ){
 27262  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27263  	}
 27264  	return rc;
 27265  }
 27266  #if defined(JX9_ENABLE_THREADS)
 27267  JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods)
 27268  {
 27269  	SyMutex *pMutex;
 27270  #if defined(UNTRUST)
 27271  	if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){
 27272  		return SXERR_CORRUPT;
 27273  	}
 27274  #endif
 27275  	pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
 27276  	if( pMutex == 0 ){
 27277  		return SXERR_OS;
 27278  	}
 27279  	/* Attach the mutex to the memory backend */
 27280  	pBackend->pMutex = pMutex;
 27281  	pBackend->pMutexMethods = pMethods;
 27282  	return SXRET_OK;
 27283  }
 27284  JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend)
 27285  {
 27286  #if defined(UNTRUST)
 27287  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27288  		return SXERR_CORRUPT;
 27289  	}
 27290  #endif
 27291  	if( pBackend->pMutex == 0 ){
 27292  		/* There is no mutex subsystem at all */
 27293  		return SXRET_OK;
 27294  	}
 27295  	SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
 27296  	pBackend->pMutexMethods = 0;
 27297  	pBackend->pMutex = 0; 
 27298  	return SXRET_OK;
 27299  }
 27300  #endif
 27301  /*
 27302   * Memory pool allocator
 27303   */
 27304  #define SXMEM_POOL_MAGIC		0xDEAD
 27305  #define SXMEM_POOL_MAXALLOC		(1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) 
 27306  #define SXMEM_POOL_MINALLOC		(1<<(SXMEM_POOL_INCR))
 27307  static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket)
 27308  {
 27309  	char *zBucket, *zBucketEnd;
 27310  	SyMemHeader *pHeader;
 27311  	sxu32 nBucketSize;
 27312  	
 27313  	/* Allocate one big block first */
 27314  	zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC);
 27315  	if( zBucket == 0 ){
 27316  		return SXERR_MEM;
 27317  	}
 27318  	zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC];
 27319  	/* Divide the big block into mini bucket pool */
 27320  	nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
 27321  	pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket;
 27322  	for(;;){
 27323  		if( &zBucket[nBucketSize] >= zBucketEnd ){
 27324  			break;
 27325  		}
 27326  		pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize];
 27327  		/* Advance the cursor to the next available chunk */
 27328  		pHeader = pHeader->pNext;
 27329  		zBucket += nBucketSize;	
 27330  	}
 27331  	pHeader->pNext = 0;
 27332  	
 27333  	return SXRET_OK;
 27334  }
 27335  static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
 27336  {
 27337  	SyMemHeader *pBucket, *pNext;
 27338  	sxu32 nBucketSize;
 27339  	sxu32 nBucket;
 27340  
 27341  	if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){
 27342  		/* Allocate a big chunk directly */
 27343  		pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader));
 27344  		if( pBucket == 0 ){
 27345  			return 0;
 27346  		}
 27347  		/* Record as big block */
 27348  		pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH;
 27349  		return (void *)(pBucket+1);
 27350  	}
 27351  	/* Locate the appropriate bucket */
 27352  	nBucket = 0;
 27353  	nBucketSize = SXMEM_POOL_MINALLOC;
 27354  	while( nByte + sizeof(SyMemHeader) > nBucketSize  ){
 27355  		nBucketSize <<= 1;
 27356  		nBucket++;
 27357  	}
 27358  	pBucket = pBackend->apPool[nBucket];
 27359  	if( pBucket == 0 ){
 27360  		sxi32 rc;
 27361  		rc = MemPoolBucketAlloc(&(*pBackend), nBucket);
 27362  		if( rc != SXRET_OK ){
 27363  			return 0;
 27364  		}
 27365  		pBucket = pBackend->apPool[nBucket];
 27366  	}
 27367  	/* Remove from the free list */
 27368  	pNext = pBucket->pNext;
 27369  	pBackend->apPool[nBucket] = pNext;
 27370  	/* Record bucket&magic number */
 27371  	pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket;
 27372  	return (void *)&pBucket[1];
 27373  }
 27374  JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
 27375  {
 27376  	void *pChunk;
 27377  #if defined(UNTRUST)
 27378  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27379  		return 0;
 27380  	}
 27381  #endif
 27382  	if( pBackend->pMutexMethods ){
 27383  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27384  	}
 27385  	pChunk = MemBackendPoolAlloc(&(*pBackend), nByte);
 27386  	if( pBackend->pMutexMethods ){
 27387  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27388  	}
 27389  	return pChunk;
 27390  }
 27391  static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
 27392  {
 27393  	SyMemHeader *pHeader;
 27394  	sxu32 nBucket;
 27395  	/* Get the corresponding bucket */
 27396  	pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader));
 27397  	/* Sanity check to avoid misuse */
 27398  	if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
 27399  		return SXERR_CORRUPT;
 27400  	}
 27401  	nBucket = pHeader->nBucket & 0xFFFF;
 27402  	if( nBucket == SXU16_HIGH ){
 27403  		/* Free the big block */
 27404  		MemBackendFree(&(*pBackend), pHeader);
 27405  	}else{
 27406  		/* Return to the free list */
 27407  		pHeader->pNext = pBackend->apPool[nBucket & 0x0f];
 27408  		pBackend->apPool[nBucket & 0x0f] = pHeader;
 27409  	}
 27410  	return SXRET_OK;
 27411  }
 27412  JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
 27413  {
 27414  	sxi32 rc;
 27415  #if defined(UNTRUST)
 27416  	if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){
 27417  		return SXERR_CORRUPT;
 27418  	}
 27419  #endif
 27420  	if( pBackend->pMutexMethods ){
 27421  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27422  	}
 27423  	rc = MemBackendPoolFree(&(*pBackend), pChunk);
 27424  	if( pBackend->pMutexMethods ){
 27425  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27426  	}
 27427  	return rc;
 27428  }
 27429  #if 0
 27430  static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
 27431  {
 27432  	sxu32 nBucket, nBucketSize;
 27433  	SyMemHeader *pHeader;
 27434  	void * pNew;
 27435  
 27436  	if( pOld == 0 ){
 27437  		/* Allocate a new pool */
 27438  		pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
 27439  		return pNew;
 27440  	}
 27441  	/* Get the corresponding bucket */
 27442  	pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader));
 27443  	/* Sanity check to avoid misuse */
 27444  	if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
 27445  		return 0;
 27446  	}
 27447  	nBucket = pHeader->nBucket & 0xFFFF;
 27448  	if( nBucket == SXU16_HIGH ){
 27449  		/* Big block */
 27450  		return MemBackendRealloc(&(*pBackend), pHeader, nByte);
 27451  	}
 27452  	nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
 27453  	if( nBucketSize >= nByte + sizeof(SyMemHeader) ){
 27454  		/* The old bucket can honor the requested size */
 27455  		return pOld;
 27456  	}
 27457  	/* Allocate a new pool */
 27458  	pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
 27459  	if( pNew == 0 ){
 27460  		return 0;
 27461  	}
 27462  	/* Copy the old data into the new block */
 27463  	SyMemcpy(pOld, pNew, nBucketSize);
 27464  	/* Free the stale block */
 27465  	MemBackendPoolFree(&(*pBackend), pOld);
 27466  	return pNew;
 27467  }
 27468  JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
 27469  {
 27470  	void *pChunk;
 27471  #if defined(UNTRUST)
 27472  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27473  		return 0;
 27474  	}
 27475  #endif
 27476  	if( pBackend->pMutexMethods ){
 27477  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27478  	}
 27479  	pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte);
 27480  	if( pBackend->pMutexMethods ){
 27481  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27482  	}
 27483  	return pChunk;
 27484  }
 27485  #endif
 27486  JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData)
 27487  {
 27488  #if defined(UNTRUST)
 27489  	if( pBackend == 0 ){
 27490  		return SXERR_EMPTY;
 27491  	}
 27492  #endif
 27493  	/* Zero the allocator first */
 27494  	SyZero(&(*pBackend), sizeof(SyMemBackend));
 27495  	pBackend->xMemError = xMemErr;
 27496  	pBackend->pUserData = pUserData;
 27497  	/* Switch to the OS memory allocator */
 27498  	pBackend->pMethods = &sOSAllocMethods;
 27499  	if( pBackend->pMethods->xInit ){
 27500  		/* Initialize the backend  */
 27501  		if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
 27502  			return SXERR_ABORT;
 27503  		}
 27504  	}
 27505  #if defined(UNTRUST)
 27506  	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
 27507  #endif
 27508  	return SXRET_OK;
 27509  }
 27510  JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData)
 27511  {
 27512  #if defined(UNTRUST)
 27513  	if( pBackend == 0 || pMethods == 0){
 27514  		return SXERR_EMPTY;
 27515  	}
 27516  #endif
 27517  	if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){
 27518  		/* mandatory methods are missing */
 27519  		return SXERR_INVALID;
 27520  	}
 27521  	/* Zero the allocator first */
 27522  	SyZero(&(*pBackend), sizeof(SyMemBackend));
 27523  	pBackend->xMemError = xMemErr;
 27524  	pBackend->pUserData = pUserData;
 27525  	/* Switch to the host application memory allocator */
 27526  	pBackend->pMethods = pMethods;
 27527  	if( pBackend->pMethods->xInit ){
 27528  		/* Initialize the backend  */
 27529  		if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
 27530  			return SXERR_ABORT;
 27531  		}
 27532  	}
 27533  #if defined(UNTRUST)
 27534  	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
 27535  #endif
 27536  	return SXRET_OK;
 27537  }
 27538  JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent)
 27539  {
 27540  	sxu8 bInheritMutex;
 27541  #if defined(UNTRUST)
 27542  	if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){
 27543  		return SXERR_CORRUPT;
 27544  	}
 27545  #endif
 27546  	/* Zero the allocator first */
 27547  	SyZero(&(*pBackend), sizeof(SyMemBackend));
 27548  	pBackend->pMethods  = pParent->pMethods;
 27549  	pBackend->xMemError = pParent->xMemError;
 27550  	pBackend->pUserData = pParent->pUserData;
 27551  	bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE;
 27552  	if( bInheritMutex ){
 27553  		pBackend->pMutexMethods = pParent->pMutexMethods;
 27554  		/* Create a private mutex */
 27555  		pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST);
 27556  		if( pBackend->pMutex ==  0){
 27557  			return SXERR_OS;
 27558  		}
 27559  	}
 27560  #if defined(UNTRUST)
 27561  	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
 27562  #endif
 27563  	return SXRET_OK;
 27564  }
 27565  static sxi32 MemBackendRelease(SyMemBackend *pBackend)
 27566  {
 27567  	SyMemBlock *pBlock, *pNext;
 27568  
 27569  	pBlock = pBackend->pBlocks;
 27570  	for(;;){
 27571  		if( pBackend->nBlock == 0 ){
 27572  			break;
 27573  		}
 27574  		pNext  = pBlock->pNext;
 27575  		pBackend->pMethods->xFree(pBlock);
 27576  		pBlock = pNext;
 27577  		pBackend->nBlock--;
 27578  		/* LOOP ONE */
 27579  		if( pBackend->nBlock == 0 ){
 27580  			break;
 27581  		}
 27582  		pNext  = pBlock->pNext;
 27583  		pBackend->pMethods->xFree(pBlock);
 27584  		pBlock = pNext;
 27585  		pBackend->nBlock--;
 27586  		/* LOOP TWO */
 27587  		if( pBackend->nBlock == 0 ){
 27588  			break;
 27589  		}
 27590  		pNext  = pBlock->pNext;
 27591  		pBackend->pMethods->xFree(pBlock);
 27592  		pBlock = pNext;
 27593  		pBackend->nBlock--;
 27594  		/* LOOP THREE */
 27595  		if( pBackend->nBlock == 0 ){
 27596  			break;
 27597  		}
 27598  		pNext  = pBlock->pNext;
 27599  		pBackend->pMethods->xFree(pBlock);
 27600  		pBlock = pNext;
 27601  		pBackend->nBlock--;
 27602  		/* LOOP FOUR */
 27603  	}
 27604  	if( pBackend->pMethods->xRelease ){
 27605  		pBackend->pMethods->xRelease(pBackend->pMethods->pUserData);
 27606  	}
 27607  	pBackend->pMethods = 0;
 27608  	pBackend->pBlocks  = 0;
 27609  #if defined(UNTRUST)
 27610  	pBackend->nMagic = 0x2626;
 27611  #endif
 27612  	return SXRET_OK;
 27613  }
 27614  JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend)
 27615  {
 27616  	sxi32 rc;
 27617  #if defined(UNTRUST)
 27618  	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
 27619  		return SXERR_INVALID;
 27620  	}
 27621  #endif
 27622  	if( pBackend->pMutexMethods ){
 27623  		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
 27624  	}
 27625  	rc = MemBackendRelease(&(*pBackend));
 27626  	if( pBackend->pMutexMethods ){
 27627  		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
 27628  		SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
 27629  	}
 27630  	return rc;
 27631  }
 27632  JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize)
 27633  {
 27634  	void *pNew;
 27635  #if defined(UNTRUST)
 27636  	if( pSrc == 0 || nSize <= 0 ){
 27637  		return 0;
 27638  	}
 27639  #endif
 27640  	pNew = SyMemBackendAlloc(&(*pBackend), nSize);
 27641  	if( pNew ){
 27642  		SyMemcpy(pSrc, pNew, nSize);
 27643  	}
 27644  	return pNew;
 27645  }
 27646  JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize)
 27647  {
 27648  	char *zDest;
 27649  	zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1);
 27650  	if( zDest ){
 27651  		Systrcpy(zDest, nSize+1, zSrc, nSize);
 27652  	}
 27653  	return zDest;
 27654  }
 27655  JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize)
 27656  {
 27657  #if defined(UNTRUST)
 27658  	if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){
 27659  		return SXERR_EMPTY;
 27660  	}
 27661  #endif
 27662  	pBlob->pBlob = pBuffer;
 27663  	pBlob->mByte = nSize;
 27664  	pBlob->nByte = 0;
 27665  	pBlob->pAllocator = 0;
 27666  	pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC;
 27667  	return SXRET_OK;
 27668  }
 27669  JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator)
 27670  {
 27671  #if defined(UNTRUST)
 27672  	if( pBlob == 0  ){
 27673  		return SXERR_EMPTY;
 27674  	}
 27675  #endif
 27676  	pBlob->pBlob = 0;
 27677  	pBlob->mByte = pBlob->nByte	= 0;
 27678  	pBlob->pAllocator = &(*pAllocator);
 27679  	pBlob->nFlags = 0;
 27680  	return SXRET_OK;
 27681  }
 27682  JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte)
 27683  {
 27684  #if defined(UNTRUST)
 27685  	if( pBlob == 0  ){
 27686  		return SXERR_EMPTY;
 27687  	}
 27688  #endif
 27689  	pBlob->pBlob = (void *)pData;
 27690  	pBlob->nByte = nByte;
 27691  	pBlob->mByte = 0;
 27692  	pBlob->nFlags |= SXBLOB_RDONLY;
 27693  	return SXRET_OK;
 27694  }
 27695  #ifndef SXBLOB_MIN_GROWTH
 27696  #define SXBLOB_MIN_GROWTH 16
 27697  #endif
 27698  static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte)
 27699  {
 27700  	sxu32 nByte;
 27701  	void *pNew;
 27702  	nByte = *pByte;
 27703  	if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){
 27704  		if ( SyBlobFreeSpace(pBlob) < nByte ){
 27705  			*pByte = SyBlobFreeSpace(pBlob);
 27706  			if( (*pByte) == 0 ){
 27707  				return SXERR_SHORT;
 27708  			}
 27709  		}
 27710  		return SXRET_OK;
 27711  	}
 27712  	if( pBlob->nFlags & SXBLOB_RDONLY ){
 27713  		/* Make a copy of the read-only item */
 27714  		if( pBlob->nByte > 0 ){
 27715  			pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte);
 27716  			if( pNew == 0 ){
 27717  				return SXERR_MEM;
 27718  			}
 27719  			pBlob->pBlob = pNew;
 27720  			pBlob->mByte = pBlob->nByte;
 27721  		}else{
 27722  			pBlob->pBlob = 0;
 27723  			pBlob->mByte = 0;
 27724  		}
 27725  		/* Remove the read-only flag */
 27726  		pBlob->nFlags &= ~SXBLOB_RDONLY;
 27727  	}
 27728  	if( SyBlobFreeSpace(pBlob) >= nByte ){
 27729  		return SXRET_OK;
 27730  	}
 27731  	if( pBlob->mByte > 0 ){
 27732  		nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH;
 27733  	}else if ( nByte < SXBLOB_MIN_GROWTH ){
 27734  		nByte = SXBLOB_MIN_GROWTH;
 27735  	}
 27736  	pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte);
 27737  	if( pNew == 0 ){
 27738  		return SXERR_MEM;
 27739  	}
 27740  	pBlob->pBlob = pNew;
 27741  	pBlob->mByte = nByte;
 27742  	return SXRET_OK;
 27743  }
 27744  JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize)
 27745  {
 27746  	sxu8 *zBlob;
 27747  	sxi32 rc;
 27748  	if( nSize < 1 ){
 27749  		return SXRET_OK;
 27750  	}
 27751  	rc = BlobPrepareGrow(&(*pBlob), &nSize);
 27752  	if( SXRET_OK != rc ){
 27753  		return rc;
 27754  	}
 27755  	if( pData ){
 27756  		zBlob = (sxu8 *)pBlob->pBlob ;
 27757  		zBlob = &zBlob[pBlob->nByte];
 27758  		pBlob->nByte += nSize;
 27759  		SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize);
 27760  	}
 27761  	return SXRET_OK;
 27762  }
 27763  JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob)
 27764  {
 27765  	sxi32 rc;
 27766  	sxu32 n;
 27767  	n = pBlob->nByte;
 27768  	rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char));
 27769  	if (rc == SXRET_OK ){
 27770  		pBlob->nByte = n;
 27771  	}
 27772  	return rc;
 27773  }
 27774  JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest)
 27775  {
 27776  	sxi32 rc = SXRET_OK;
 27777  	if( pSrc->nByte > 0 ){
 27778  		rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte);
 27779  	}
 27780  	return rc;
 27781  }
 27782  JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob)
 27783  {
 27784  	pBlob->nByte = 0;
 27785  	if( pBlob->nFlags & SXBLOB_RDONLY ){
 27786  		/* Read-only (Not malloced chunk) */
 27787  		pBlob->pBlob = 0;
 27788  		pBlob->mByte = 0;
 27789  		pBlob->nFlags &= ~SXBLOB_RDONLY;
 27790  	}
 27791  	return SXRET_OK;
 27792  }
 27793  JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen)
 27794  {
 27795  	if( nNewLen < pBlob->nByte ){
 27796  		pBlob->nByte = nNewLen;
 27797  	}
 27798  	return SXRET_OK;
 27799  }
 27800  JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob)
 27801  {
 27802  	if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){
 27803  		SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob);
 27804  	}
 27805  	pBlob->pBlob = 0;
 27806  	pBlob->nByte = pBlob->mByte = 0;
 27807  	pBlob->nFlags = 0;
 27808  	return SXRET_OK;
 27809  }
 27810  #ifndef JX9_DISABLE_BUILTIN_FUNC
 27811  JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft)
 27812  {
 27813  	const char *zIn = (const char *)pBlob;
 27814  	const char *zEnd;
 27815  	sxi32 rc;
 27816  	if( pLen > nLen ){
 27817  		return SXERR_NOTFOUND;
 27818  	}
 27819  	zEnd = &zIn[nLen-pLen];
 27820  	for(;;){
 27821  		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++;
 27822  		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++;
 27823  		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++;
 27824  		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++;
 27825  	}
 27826  	return SXERR_NOTFOUND;
 27827  }
 27828  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 27829  /* SyRunTimeApi:sxds.c */
 27830  JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize)
 27831  {
 27832  	pSet->nSize = 0 ;
 27833  	pSet->nUsed = 0;
 27834  	pSet->nCursor = 0;
 27835  	pSet->eSize = ElemSize;
 27836  	pSet->pAllocator = pAllocator;
 27837  	pSet->pBase =  0;
 27838  	pSet->pUserData = 0;
 27839  	return SXRET_OK;
 27840  }
 27841  JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem)
 27842  {
 27843  	unsigned char *zbase;
 27844  	if( pSet->nUsed >= pSet->nSize ){
 27845  		void *pNew;
 27846  		if( pSet->pAllocator == 0 ){
 27847  			return  SXERR_LOCKED;
 27848  		}
 27849  		if( pSet->nSize <= 0 ){
 27850  			pSet->nSize = 4;
 27851  		}
 27852  		pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2);
 27853  		if( pNew == 0 ){
 27854  			return SXERR_MEM;
 27855  		}
 27856  		pSet->pBase = pNew;
 27857  		pSet->nSize <<= 1;
 27858  	}
 27859  	zbase = (unsigned char *)pSet->pBase;
 27860  	SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize);
 27861  	pSet->nUsed++;	
 27862  	return SXRET_OK;
 27863  }
 27864  JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem)
 27865  {
 27866  	if( pSet->nSize > 0 ){
 27867  		return SXERR_LOCKED;
 27868  	}
 27869  	if( nItem < 8 ){
 27870  		nItem = 8;
 27871  	}
 27872  	pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem);
 27873  	if( pSet->pBase == 0 ){
 27874  		return SXERR_MEM;
 27875  	}
 27876  	pSet->nSize = nItem;
 27877  	return SXRET_OK;
 27878  } 
 27879  JX9_PRIVATE sxi32 SySetReset(SySet *pSet)
 27880  {
 27881  	pSet->nUsed   = 0;
 27882  	pSet->nCursor = 0;
 27883  	return SXRET_OK;
 27884  }
 27885  JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet)
 27886  {
 27887  	pSet->nCursor = 0;
 27888  	return SXRET_OK;
 27889  }
 27890  JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry)
 27891  {
 27892  	register unsigned char *zSrc;
 27893  	if( pSet->nCursor >= pSet->nUsed ){
 27894  		/* Reset cursor */
 27895  		pSet->nCursor = 0;
 27896  		return SXERR_EOF;
 27897  	}
 27898  	zSrc = (unsigned char *)SySetBasePtr(pSet);
 27899  	if( ppEntry ){
 27900  		*ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize];
 27901  	}
 27902  	pSet->nCursor++;
 27903  	return SXRET_OK;
 27904  }
 27905  JX9_PRIVATE sxi32 SySetRelease(SySet *pSet)
 27906  {
 27907  	sxi32 rc = SXRET_OK;
 27908  	if( pSet->pAllocator && pSet->pBase ){
 27909  		rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase);
 27910  	}
 27911  	pSet->pBase = 0;
 27912  	pSet->nUsed = 0;
 27913  	pSet->nCursor = 0;
 27914  	return rc;
 27915  }
 27916  JX9_PRIVATE void * SySetPeek(SySet *pSet)
 27917  {
 27918  	const char *zBase;
 27919  	if( pSet->nUsed <= 0 ){
 27920  		return 0;
 27921  	}
 27922  	zBase = (const char *)pSet->pBase;
 27923  	return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize]; 
 27924  }
 27925  JX9_PRIVATE void * SySetPop(SySet *pSet)
 27926  {
 27927  	const char *zBase;
 27928  	void *pData;
 27929  	if( pSet->nUsed <= 0 ){
 27930  		return 0;
 27931  	}
 27932  	zBase = (const char *)pSet->pBase;
 27933  	pSet->nUsed--;
 27934  	pData =  (void *)&zBase[pSet->nUsed * pSet->eSize]; 
 27935  	return pData;
 27936  }
 27937  JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx)
 27938  {
 27939  	const char *zBase;
 27940  	if( nIdx >= pSet->nUsed ){
 27941  		/* Out of range */
 27942  		return 0;
 27943  	}
 27944  	zBase = (const char *)pSet->pBase;
 27945  	return (void *)&zBase[nIdx * pSet->eSize]; 
 27946  }
 27947  /* Private hash entry */
 27948  struct SyHashEntry_Pr
 27949  {
 27950  	const void *pKey; /* Hash key */
 27951  	sxu32 nKeyLen;    /* Key length */
 27952  	void *pUserData;  /* User private data */
 27953  	/* Private fields */
 27954  	sxu32 nHash;
 27955  	SyHash *pHash;
 27956  	SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */
 27957  	SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */
 27958  };
 27959  #define INVALID_HASH(H) ((H)->apBucket == 0)
 27960  JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp)
 27961  {
 27962  	SyHashEntry_Pr **apNew;
 27963  #if defined(UNTRUST)
 27964  	if( pHash == 0 ){
 27965  		return SXERR_EMPTY;
 27966  	}
 27967  #endif
 27968  	/* Allocate a new table */
 27969  	apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
 27970  	if( apNew == 0 ){
 27971  		return SXERR_MEM;
 27972  	}
 27973  	SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
 27974  	pHash->pAllocator = &(*pAllocator);
 27975  	pHash->xHash = xHash ? xHash : SyBinHash;
 27976  	pHash->xCmp = xCmp ? xCmp : SyMemcmp;
 27977  	pHash->pCurrent = pHash->pList = 0;
 27978  	pHash->nEntry = 0;
 27979  	pHash->apBucket = apNew;
 27980  	pHash->nBucketSize = SXHASH_BUCKET_SIZE;
 27981  	return SXRET_OK;
 27982  }
 27983  JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash)
 27984  {
 27985  	SyHashEntry_Pr *pEntry, *pNext;
 27986  #if defined(UNTRUST)
 27987  	if( INVALID_HASH(pHash)  ){
 27988  		return SXERR_EMPTY;
 27989  	}
 27990  #endif
 27991  	pEntry = pHash->pList;
 27992  	for(;;){
 27993  		if( pHash->nEntry == 0 ){
 27994  			break;
 27995  		}
 27996  		pNext = pEntry->pNext;
 27997  		SyMemBackendPoolFree(pHash->pAllocator, pEntry);
 27998  		pEntry = pNext;
 27999  		pHash->nEntry--;
 28000  	}
 28001  	if( pHash->apBucket ){
 28002  		SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
 28003  	}
 28004  	pHash->apBucket = 0;
 28005  	pHash->nBucketSize = 0;
 28006  	pHash->pAllocator = 0;
 28007  	return SXRET_OK;
 28008  }
 28009  static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
 28010  {
 28011  	SyHashEntry_Pr *pEntry;
 28012  	sxu32 nHash;
 28013  
 28014  	nHash = pHash->xHash(pKey, nKeyLen);
 28015  	pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)];
 28016  	for(;;){
 28017  		if( pEntry == 0 ){
 28018  			break;
 28019  		}
 28020  		if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen && 
 28021  			pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){
 28022  				return pEntry;
 28023  		}
 28024  		pEntry = pEntry->pNextCollide;
 28025  	}
 28026  	/* Entry not found */
 28027  	return 0;
 28028  }
 28029  JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
 28030  {
 28031  	SyHashEntry_Pr *pEntry;
 28032  #if defined(UNTRUST)
 28033  	if( INVALID_HASH(pHash) ){
 28034  		return 0;
 28035  	}
 28036  #endif
 28037  	if( pHash->nEntry < 1 || nKeyLen < 1 ){
 28038  		/* Don't bother hashing, return immediately */
 28039  		return 0;
 28040  	}
 28041  	pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
 28042  	if( pEntry == 0 ){
 28043  		return 0;
 28044  	}
 28045  	return (SyHashEntry *)pEntry;
 28046  }
 28047  static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData)
 28048  {
 28049  	sxi32 rc;
 28050  	if( pEntry->pPrevCollide == 0 ){
 28051  		pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide;
 28052  	}else{
 28053  		pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
 28054  	}
 28055  	if( pEntry->pNextCollide ){
 28056  		pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
 28057  	}
 28058  	MACRO_LD_REMOVE(pHash->pList, pEntry);
 28059  	pHash->nEntry--;
 28060  	if( ppUserData ){
 28061  		/* Write a pointer to the user data */
 28062  		*ppUserData = pEntry->pUserData;
 28063  	}
 28064  	/* Release the entry */
 28065  	rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry);
 28066  	return rc;
 28067  }
 28068  JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData)
 28069  {
 28070  	SyHashEntry_Pr *pEntry;
 28071  	sxi32 rc;
 28072  #if defined(UNTRUST)
 28073  	if( INVALID_HASH(pHash) ){
 28074  		return SXERR_CORRUPT;
 28075  	}
 28076  #endif
 28077  	pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
 28078  	if( pEntry == 0 ){
 28079  		return SXERR_NOTFOUND;
 28080  	}
 28081  	rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData);
 28082  	return rc;
 28083  }
 28084  JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData)
 28085  {
 28086  	SyHashEntry_Pr *pEntry;
 28087  	sxi32 rc;
 28088  	sxu32 n;
 28089  #if defined(UNTRUST)
 28090  	if( INVALID_HASH(pHash) || xStep == 0){
 28091  		return 0;
 28092  	}
 28093  #endif
 28094  	pEntry = pHash->pList;
 28095  	for( n = 0 ; n < pHash->nEntry ; n++ ){
 28096  		/* Invoke the callback */
 28097  		rc = xStep((SyHashEntry *)pEntry, pUserData);
 28098  		if( rc != SXRET_OK ){
 28099  			return rc;
 28100  		}
 28101  		/* Point to the next entry */
 28102  		pEntry = pEntry->pNext;
 28103  	}
 28104  	return SXRET_OK;
 28105  }
 28106  static sxi32 HashGrowTable(SyHash *pHash)
 28107  {
 28108  	sxu32 nNewSize = pHash->nBucketSize * 2;
 28109  	SyHashEntry_Pr *pEntry;
 28110  	SyHashEntry_Pr **apNew;
 28111  	sxu32 n, iBucket;
 28112  
 28113  	/* Allocate a new larger table */
 28114  	apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *));
 28115  	if( apNew == 0 ){
 28116  		/* Not so fatal, simply a performance hit */
 28117  		return SXRET_OK;
 28118  	}
 28119  	/* Zero the new table */
 28120  	SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *));
 28121  	/* Rehash all entries */
 28122  	for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++  ){
 28123  		pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 28124  		/* Install in the new bucket */
 28125  		iBucket = pEntry->nHash & (nNewSize - 1);
 28126  		pEntry->pNextCollide = apNew[iBucket];
 28127  		if( apNew[iBucket] != 0 ){
 28128  			apNew[iBucket]->pPrevCollide = pEntry;
 28129  		}
 28130  		apNew[iBucket] = pEntry;
 28131  		/* Point to the next entry */
 28132  		pEntry = pEntry->pNext;
 28133  	}
 28134  	/* Release the old table and reflect the change */
 28135  	SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
 28136  	pHash->apBucket = apNew;
 28137  	pHash->nBucketSize = nNewSize;
 28138  	return SXRET_OK;
 28139  }
 28140  static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry)
 28141  {
 28142  	sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1);
 28143  	/* Insert the entry in its corresponding bcuket */
 28144  	pEntry->pNextCollide = pHash->apBucket[iBucket];
 28145  	if( pHash->apBucket[iBucket] != 0 ){
 28146  		pHash->apBucket[iBucket]->pPrevCollide = pEntry;
 28147  	}
 28148  	pHash->apBucket[iBucket] = pEntry;
 28149  	/* Link to the entry list */
 28150  	MACRO_LD_PUSH(pHash->pList, pEntry);
 28151  	if( pHash->nEntry == 0 ){
 28152  		pHash->pCurrent = pHash->pList;
 28153  	}
 28154  	pHash->nEntry++;
 28155  	return SXRET_OK;
 28156  }
 28157  JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData)
 28158  {
 28159  	SyHashEntry_Pr *pEntry;
 28160  	sxi32 rc;
 28161  #if defined(UNTRUST)
 28162  	if( INVALID_HASH(pHash) || pKey == 0 ){
 28163  		return SXERR_CORRUPT;
 28164  	}
 28165  #endif
 28166  	if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){
 28167  		rc = HashGrowTable(&(*pHash));
 28168  		if( rc != SXRET_OK ){
 28169  			return rc;
 28170  		}
 28171  	}
 28172  	/* Allocate a new hash entry */
 28173  	pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr));
 28174  	if( pEntry == 0 ){
 28175  		return SXERR_MEM;
 28176  	}
 28177  	/* Zero the entry */
 28178  	SyZero(pEntry, sizeof(SyHashEntry_Pr));
 28179  	pEntry->pHash = pHash;
 28180  	pEntry->pKey = pKey;
 28181  	pEntry->nKeyLen = nKeyLen;
 28182  	pEntry->pUserData = pUserData;
 28183  	pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen);
 28184  	/* Finally insert the entry in its corresponding bucket */
 28185  	rc = HashInsert(&(*pHash), pEntry);
 28186  	return rc;
 28187  }
 28188  /* SyRunTimeApi:sxutils.c */
 28189  JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char  **pzTail)
 28190  {
 28191  	const char *zCur, *zEnd;
 28192  #ifdef UNTRUST
 28193  	if( SX_EMPTY_STR(zSrc) ){
 28194  		return SXERR_EMPTY;
 28195  	}
 28196  #endif
 28197  	zEnd = &zSrc[nLen];
 28198  	/* Jump leading white spaces */
 28199  	while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0  && SyisSpace(zSrc[0]) ){
 28200  		zSrc++;
 28201  	}
 28202  	if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
 28203  		zSrc++;
 28204  	}
 28205  	zCur = zSrc;
 28206  	if( pReal ){
 28207  		*pReal = FALSE;
 28208  	}
 28209  	for(;;){
 28210  		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
 28211  		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
 28212  		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
 28213  		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
 28214  	};
 28215  	if( zSrc < zEnd && zSrc > zCur ){
 28216  		int c = zSrc[0];
 28217  		if( c == '.' ){
 28218  			zSrc++;
 28219  			if( pReal ){
 28220  				*pReal = TRUE;
 28221  			}
 28222  			if( pzTail ){
 28223  				while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
 28224  					zSrc++;
 28225  				}
 28226  				if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){
 28227  					zSrc++;
 28228  					if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
 28229  						zSrc++;
 28230  					}
 28231  					while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
 28232  						zSrc++;
 28233  					}
 28234  				}
 28235  			}
 28236  		}else if( c == 'e' || c == 'E' ){
 28237  			zSrc++;
 28238  			if( pReal ){
 28239  				*pReal = TRUE;
 28240  			}
 28241  			if( pzTail ){
 28242  				if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
 28243  					zSrc++;
 28244  				}
 28245  				while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
 28246  					zSrc++;
 28247  				}
 28248  			}
 28249  		}
 28250  	}
 28251  	if( pzTail ){
 28252  		/* Point to the non numeric part */
 28253  		*pzTail = zSrc;
 28254  	}
 28255  	return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
 28256  }
 28257  #define SXINT32_MIN_STR		"2147483648"
 28258  #define SXINT32_MAX_STR		"2147483647"
 28259  #define SXINT64_MIN_STR		"9223372036854775808"
 28260  #define SXINT64_MAX_STR		"9223372036854775807"
 28261  JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28262  {
 28263  	int isNeg = FALSE;
 28264  	const char *zEnd;
 28265  	sxi32 nVal = 0;
 28266  	sxi16 i;
 28267  #if defined(UNTRUST)
 28268  	if( SX_EMPTY_STR(zSrc) ){
 28269  		if( pOutVal ){
 28270  			*(sxi32 *)pOutVal = 0;
 28271  		}
 28272  		return SXERR_EMPTY;
 28273  	}
 28274  #endif
 28275  	zEnd = &zSrc[nLen];
 28276  	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28277  		zSrc++;
 28278  	}
 28279  	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
 28280  		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
 28281  		zSrc++;
 28282  	}
 28283  	/* Skip leading zero */
 28284  	while(zSrc < zEnd && zSrc[0] == '0' ){
 28285  		zSrc++; 
 28286  	}
 28287  	i = 10;
 28288  	if( (sxu32)(zEnd-zSrc) >= 10 ){
 28289  		/* Handle overflow */
 28290  		i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9; 
 28291  	}
 28292  	for(;;){
 28293  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28294  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28295  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28296  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28297  	}
 28298  	/* Skip trailing spaces */
 28299  	while(zSrc < zEnd && SyisSpace(zSrc[0])){
 28300  		zSrc++;
 28301  	}
 28302  	if( zRest ){
 28303  		*zRest = (char *)zSrc;
 28304  	}	
 28305  	if( pOutVal ){
 28306  		if( isNeg == TRUE && nVal != 0 ){
 28307  			nVal = -nVal;
 28308  		}
 28309  		*(sxi32 *)pOutVal = nVal;
 28310  	}
 28311  	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
 28312  }
 28313  JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28314  {
 28315  	int isNeg = FALSE;
 28316  	const char *zEnd;
 28317  	sxi64 nVal;
 28318  	sxi16 i;
 28319  #if defined(UNTRUST)
 28320  	if( SX_EMPTY_STR(zSrc) ){
 28321  		if( pOutVal ){
 28322  			*(sxi32 *)pOutVal = 0;
 28323  		}
 28324  		return SXERR_EMPTY;
 28325  	}
 28326  #endif
 28327  	zEnd = &zSrc[nLen];
 28328  	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28329  		zSrc++;
 28330  	}
 28331  	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
 28332  		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
 28333  		zSrc++;
 28334  	}
 28335  	/* Skip leading zero */
 28336  	while(zSrc < zEnd && zSrc[0] == '0' ){
 28337  		zSrc++;
 28338  	}
 28339  	i = 19;
 28340  	if( (sxu32)(zEnd-zSrc) >= 19 ){
 28341  		i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
 28342  	}
 28343  	nVal = 0;
 28344  	for(;;){
 28345  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28346  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28347  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28348  		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
 28349  	}
 28350  	/* Skip trailing spaces */
 28351  	while(zSrc < zEnd && SyisSpace(zSrc[0])){
 28352  		zSrc++;
 28353  	}
 28354  	if( zRest ){
 28355  		*zRest = (char *)zSrc;
 28356  	}	
 28357  	if( pOutVal ){
 28358  		if( isNeg == TRUE && nVal != 0 ){
 28359  			nVal = -nVal;
 28360  		}
 28361  		*(sxi64 *)pOutVal = nVal;
 28362  	}
 28363  	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
 28364  }
 28365  JX9_PRIVATE sxi32 SyHexToint(sxi32 c)
 28366  {
 28367  	switch(c){
 28368  	case '0': return 0;
 28369  	case '1': return 1;
 28370  	case '2': return 2;
 28371  	case '3': return 3;
 28372  	case '4': return 4;
 28373  	case '5': return 5;
 28374  	case '6': return 6;
 28375  	case '7': return 7;
 28376  	case '8': return 8;
 28377  	case '9': return 9;
 28378  	case 'A': case 'a': return 10;
 28379  	case 'B': case 'b': return 11;
 28380  	case 'C': case 'c': return 12;
 28381  	case 'D': case 'd': return 13;
 28382  	case 'E': case 'e': return 14;
 28383  	case 'F': case 'f': return 15;
 28384  	}
 28385  	return -1; 	
 28386  }
 28387  JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28388  {
 28389  	const char *zIn, *zEnd;
 28390  	int isNeg = FALSE;
 28391  	sxi64 nVal = 0;
 28392  #if defined(UNTRUST)
 28393  	if( SX_EMPTY_STR(zSrc) ){
 28394  		if( pOutVal ){
 28395  			*(sxi32 *)pOutVal = 0;
 28396  		}
 28397  		return SXERR_EMPTY;
 28398  	}
 28399  #endif
 28400  	zEnd = &zSrc[nLen];
 28401  	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28402  		zSrc++;
 28403  	}
 28404  	if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){
 28405  		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
 28406  		zSrc++;
 28407  	}
 28408  	if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){
 28409  		/* Bypass hex prefix */
 28410  		zSrc += sizeof(char) * 2;
 28411  	}	
 28412  	/* Skip leading zero */
 28413  	while(zSrc < zEnd && zSrc[0] == '0' ){
 28414  		zSrc++;
 28415  	}
 28416  	zIn = zSrc;
 28417  	for(;;){
 28418  		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
 28419  		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
 28420  		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
 28421  		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
 28422  	}
 28423  	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28424  		zSrc++;
 28425  	}	
 28426  	if( zRest ){
 28427  		*zRest = zSrc;
 28428  	}
 28429  	if( pOutVal ){
 28430  		if( isNeg == TRUE && nVal != 0 ){
 28431  			nVal = -nVal;
 28432  		}
 28433  		*(sxi64 *)pOutVal = nVal;
 28434  	}
 28435  	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
 28436  }
 28437  JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28438  {
 28439  	const char *zIn, *zEnd;
 28440  	int isNeg = FALSE;
 28441  	sxi64 nVal = 0;
 28442  	int c;
 28443  #if defined(UNTRUST)
 28444  	if( SX_EMPTY_STR(zSrc) ){
 28445  		if( pOutVal ){
 28446  			*(sxi32 *)pOutVal = 0;
 28447  		}
 28448  		return SXERR_EMPTY;
 28449  	}
 28450  #endif
 28451  	zEnd = &zSrc[nLen];
 28452  	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28453  		zSrc++;
 28454  	}
 28455  	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
 28456  		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
 28457  		zSrc++;
 28458  	}
 28459  	/* Skip leading zero */
 28460  	while(zSrc < zEnd && zSrc[0] == '0' ){
 28461  		zSrc++; 
 28462  	}
 28463  	zIn = zSrc;
 28464  	for(;;){
 28465  		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
 28466  		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
 28467  		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
 28468  		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
 28469  	}
 28470  	/* Skip trailing spaces */
 28471  	while(zSrc < zEnd && SyisSpace(zSrc[0])){
 28472  		zSrc++;
 28473  	}
 28474  	if( zRest ){
 28475  		*zRest = zSrc;
 28476  	}	
 28477  	if( pOutVal ){
 28478  		if( isNeg == TRUE && nVal != 0 ){
 28479  			nVal = -nVal;
 28480  		}
 28481  		*(sxi64 *)pOutVal = nVal;
 28482  	}
 28483  	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
 28484  }
 28485  JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28486  {
 28487  	const char *zIn, *zEnd;
 28488  	int isNeg = FALSE;
 28489  	sxi64 nVal = 0;
 28490  	int c;
 28491  #if defined(UNTRUST)
 28492  	if( SX_EMPTY_STR(zSrc) ){
 28493  		if( pOutVal ){
 28494  			*(sxi32 *)pOutVal = 0;
 28495  		}
 28496  		return SXERR_EMPTY;
 28497  	}
 28498  #endif
 28499  	zEnd = &zSrc[nLen];
 28500  	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28501  		zSrc++;
 28502  	}
 28503  	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
 28504  		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
 28505  		zSrc++;
 28506  	}
 28507  	if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){
 28508  		/* Bypass binary prefix */
 28509  		zSrc += sizeof(char) * 2;
 28510  	}
 28511  	/* Skip leading zero */
 28512  	while(zSrc < zEnd && zSrc[0] == '0' ){
 28513  		zSrc++; 
 28514  	}
 28515  	zIn = zSrc;
 28516  	for(;;){
 28517  		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
 28518  		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
 28519  		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
 28520  		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
 28521  	}
 28522  	/* Skip trailing spaces */
 28523  	while(zSrc < zEnd && SyisSpace(zSrc[0])){
 28524  		zSrc++;
 28525  	}
 28526  	if( zRest ){
 28527  		*zRest = zSrc;
 28528  	}	
 28529  	if( pOutVal ){
 28530  		if( isNeg == TRUE && nVal != 0 ){
 28531  			nVal = -nVal;
 28532  		}
 28533  		*(sxi64 *)pOutVal = nVal;
 28534  	}
 28535  	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
 28536  }
 28537  JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
 28538  {
 28539  #define SXDBL_DIG        15
 28540  #define SXDBL_MAX_EXP    308
 28541  #define SXDBL_MIN_EXP_PLUS	307
 28542  	static const sxreal aTab[] = {
 28543  	10, 
 28544  	1.0e2, 
 28545  	1.0e4, 
 28546  	1.0e8, 
 28547  	1.0e16, 
 28548  	1.0e32, 
 28549  	1.0e64, 
 28550  	1.0e128, 
 28551  	1.0e256
 28552  	};
 28553  	sxu8 neg = FALSE;
 28554  	sxreal Val = 0.0;
 28555  	const char *zEnd;
 28556  	sxi32 Lim, exp;
 28557  	sxreal *p = 0;
 28558  #ifdef UNTRUST
 28559  	if( SX_EMPTY_STR(zSrc)  ){
 28560  		if( pOutVal ){
 28561  			*(sxreal *)pOutVal = 0.0;
 28562  		}
 28563  		return SXERR_EMPTY;
 28564  	}
 28565  #endif
 28566  	zEnd = &zSrc[nLen];
 28567  	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28568  		zSrc++; 
 28569  	}
 28570  	if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){
 28571  		neg =  zSrc[0] == '-' ? TRUE : FALSE ;
 28572  		zSrc++;
 28573  	}
 28574  	Lim = SXDBL_DIG ;
 28575  	for(;;){
 28576  		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
 28577  		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
 28578  		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
 28579  		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
 28580  	}
 28581  	if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){
 28582  		sxreal dec = 1.0;
 28583  		zSrc++;
 28584  		for(;;){
 28585  			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
 28586  			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
 28587  			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
 28588  			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
 28589  		}
 28590  		Val /= dec;
 28591  	}
 28592  	if( neg == TRUE && Val != 0.0 ) {
 28593  		Val = -Val ; 
 28594  	}
 28595  	if( Lim <= 0 ){
 28596  		/* jump overflow digit */
 28597  		while( zSrc < zEnd ){
 28598  			if( zSrc[0] == 'e' || zSrc[0] == 'E' ){
 28599  				break;  
 28600  			}
 28601  			zSrc++;
 28602  		}
 28603  	}
 28604  	neg = FALSE;
 28605  	if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){
 28606  		zSrc++;
 28607  		if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){
 28608  			neg = zSrc[0] == '-' ? TRUE : FALSE ;
 28609  			zSrc++;
 28610  		}
 28611  		exp = 0;
 28612  		while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){
 28613  			exp = exp * 10 + (zSrc[0] - '0');
 28614  			zSrc++;
 28615  		}
 28616  		if( neg  ){
 28617  			if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ;
 28618  		}else if ( exp > SXDBL_MAX_EXP ){
 28619  			exp = SXDBL_MAX_EXP; 
 28620  		}		
 28621  		for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){
 28622  			if( exp & 01 ){
 28623  				if( neg ){
 28624  					Val /= *p ;
 28625  				}else{
 28626  					Val *= *p;
 28627  				}
 28628  			}
 28629  		}
 28630  	}
 28631  	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
 28632  		zSrc++;
 28633  	}
 28634  	if( zRest ){
 28635  		*zRest = zSrc; 
 28636  	}
 28637  	if( pOutVal ){
 28638  		*(sxreal *)pOutVal = Val;
 28639  	}
 28640  	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
 28641  }
 28642  /* SyRunTimeApi:sxlib.c  */
 28643  JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen)
 28644  {
 28645  	register unsigned char *zIn = (unsigned char *)pSrc;
 28646  	unsigned char *zEnd;
 28647  	sxu32 nH = 5381;
 28648  	zEnd = &zIn[nLen];
 28649  	for(;;){
 28650  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 28651  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 28652  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 28653  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 28654  	}	
 28655  	return nH;
 28656  }
 28657  #ifndef JX9_DISABLE_BUILTIN_FUNC
 28658  JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
 28659  {
 28660  	static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 28661  	unsigned char *zIn = (unsigned char *)zSrc;
 28662  	unsigned char z64[4];
 28663  	sxu32 i;
 28664  	sxi32 rc;
 28665  #if defined(UNTRUST)
 28666  	if( SX_EMPTY_STR(zSrc) || xConsumer == 0){
 28667  		return SXERR_EMPTY;
 28668  	}
 28669  #endif
 28670  	for(i = 0; i + 2 < nLen; i += 3){
 28671  		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
 28672  		z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F]; 
 28673  		z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F];
 28674  		z64[3] = zBase64[ zIn[i + 2] & 0x3F];
 28675  		
 28676  		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
 28677  		if( rc != SXRET_OK ){return SXERR_ABORT;}
 28678  
 28679  	}	
 28680  	if ( i+1 < nLen ){
 28681  		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
 28682  		z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F]; 
 28683  		z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ];
 28684  		z64[3] = '=';
 28685  		
 28686  		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
 28687  		if( rc != SXRET_OK ){return SXERR_ABORT;}
 28688  
 28689  	}else if( i < nLen ){
 28690  		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
 28691  		z64[1]   = zBase64[(zIn[i] & 0x03) << 4];
 28692  		z64[2] = '=';
 28693  		z64[3] = '=';
 28694  		
 28695  		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
 28696  		if( rc != SXRET_OK ){return SXERR_ABORT;}
 28697  	}
 28698  
 28699  	return SXRET_OK;
 28700  }
 28701  JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
 28702  {
 28703  	static const sxu32 aBase64Trans[] = {
 28704  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 28705  	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, 
 28706  	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, 
 28707  	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, 
 28708  	0, 0, 0
 28709  	};
 28710  	sxu32 n, w, x, y, z;
 28711  	sxi32 rc;
 28712  	unsigned char zOut[10];
 28713  #if defined(UNTRUST)
 28714  	if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){
 28715  		return SXERR_EMPTY;
 28716  	}
 28717  #endif
 28718  	while(nLen > 0 && zB64[nLen - 1] == '=' ){
 28719  		nLen--;
 28720  	}
 28721  	for( n = 0 ; n+3<nLen ; n += 4){
 28722  		w = aBase64Trans[zB64[n] & 0x7F];
 28723  		x = aBase64Trans[zB64[n+1] & 0x7F];
 28724  		y = aBase64Trans[zB64[n+2] & 0x7F];
 28725  		z = aBase64Trans[zB64[n+3] & 0x7F];
 28726  		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
 28727  		zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
 28728  		zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F);
 28729  
 28730  		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData);
 28731  		if( rc != SXRET_OK ){ return SXERR_ABORT;}
 28732  	}
 28733  	if( n+2 < nLen ){
 28734  		w = aBase64Trans[zB64[n] & 0x7F];
 28735  		x = aBase64Trans[zB64[n+1] & 0x7F];
 28736  		y = aBase64Trans[zB64[n+2] & 0x7F];
 28737  
 28738  		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
 28739  		zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
 28740  
 28741  		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData);
 28742  		if( rc != SXRET_OK ){ return SXERR_ABORT;}
 28743  	}else if( n+1 < nLen ){
 28744  		w = aBase64Trans[zB64[n] & 0x7F];
 28745  		x = aBase64Trans[zB64[n+1] & 0x7F];
 28746  
 28747  		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
 28748  
 28749  		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData);
 28750  		if( rc != SXRET_OK ){ return SXERR_ABORT;}
 28751  	}
 28752  	return SXRET_OK;
 28753  }
 28754  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 28755  #define INVALID_LEXER(LEX)	(  LEX == 0  || LEX->xTokenizer == 0 )
 28756  JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData)
 28757  {
 28758  	SyStream *pStream;
 28759  #if defined (UNTRUST)
 28760  	if ( pLex == 0 || xTokenizer == 0 ){
 28761  		return SXERR_CORRUPT;
 28762  	}
 28763  #endif
 28764  	pLex->pTokenSet = 0;
 28765  	/* Initialize lexer fields */
 28766  	if( pSet ){
 28767  		if ( SySetElemSize(pSet) != sizeof(SyToken) ){
 28768  			return SXERR_INVALID;
 28769  		}
 28770  		pLex->pTokenSet = pSet;
 28771  	}
 28772  	pStream = &pLex->sStream;
 28773  	pLex->xTokenizer = xTokenizer;
 28774  	pLex->pUserData = pUserData;
 28775  	
 28776  	pStream->nLine = 1;
 28777  	pStream->nIgn  = 0;
 28778  	pStream->zText = pStream->zEnd = 0;
 28779  	pStream->pSet  = pSet;
 28780  	return SXRET_OK;
 28781  }
 28782  JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp)
 28783  {
 28784  	const unsigned char *zCur;
 28785  	SyStream *pStream;
 28786  	SyToken sToken;
 28787  	sxi32 rc;
 28788  #if defined (UNTRUST)
 28789  	if ( INVALID_LEXER(pLex) || zInput == 0 ){
 28790  		return SXERR_CORRUPT;
 28791  	}
 28792  #endif
 28793  	pStream = &pLex->sStream;
 28794  	/* Point to the head of the input */
 28795  	pStream->zText = pStream->zInput = (const unsigned char *)zInput;
 28796  	/* Point to the end of the input */
 28797  	pStream->zEnd = &pStream->zInput[nLen];
 28798  	for(;;){
 28799  		if( pStream->zText >= pStream->zEnd ){
 28800  			/* End of the input reached */
 28801  			break;
 28802  		}
 28803  		zCur = pStream->zText;
 28804  		/* Call the tokenizer callback */
 28805  		rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData);
 28806  		if( rc != SXRET_OK && rc != SXERR_CONTINUE ){
 28807  			/* Tokenizer callback request an operation abort */
 28808  			if( rc == SXERR_ABORT ){
 28809  				return SXERR_ABORT;
 28810  			}
 28811  			break;
 28812  		}
 28813  		if( rc == SXERR_CONTINUE ){
 28814  			/* Request to ignore this token */
 28815  			pStream->nIgn++;
 28816  		}else if( pLex->pTokenSet  ){
 28817  			/* Put the token in the set */
 28818  			rc = SySetPut(pLex->pTokenSet, (const void *)&sToken);
 28819  			if( rc != SXRET_OK ){
 28820  				break;
 28821  			}
 28822  		}
 28823  		if( zCur >= pStream->zText ){
 28824  			/* Automatic advance of the stream cursor */
 28825  			pStream->zText = &zCur[1];
 28826  		}
 28827  	}
 28828  	if( xSort &&  pLex->pTokenSet ){
 28829  		SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet);
 28830  		/* Sort the extrated tokens */
 28831  		if( xCmp == 0 ){
 28832  			/* Use a default comparison function */
 28833  			xCmp = SyMemcmp;
 28834  		}
 28835  		xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp);
 28836  	}
 28837  	return SXRET_OK;
 28838  }
 28839  JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex)
 28840  {
 28841  	sxi32 rc = SXRET_OK;
 28842  #if defined (UNTRUST)
 28843  	if ( INVALID_LEXER(pLex) ){
 28844  		return SXERR_CORRUPT;
 28845  	}
 28846  #else
 28847  	SXUNUSED(pLex); /* Prevent compiler warning */
 28848  #endif
 28849  	return rc;
 28850  }
 28851  #ifndef JX9_DISABLE_BUILTIN_FUNC
 28852  #define SAFE_HTTP(C)	(SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' )
 28853  JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
 28854  {
 28855  	unsigned char *zIn = (unsigned char *)zSrc;
 28856  	unsigned char zHex[3] = { '%', 0, 0 };
 28857  	unsigned char zOut[2];
 28858  	unsigned char *zCur, *zEnd;
 28859  	sxi32 c;
 28860  	sxi32 rc;
 28861  #ifdef UNTRUST
 28862  	if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
 28863  		return SXERR_EMPTY;
 28864  	}
 28865  #endif
 28866  	rc = SXRET_OK;
 28867  	zEnd = &zIn[nLen]; zCur = zIn;
 28868  	for(;;){
 28869  		if( zCur >= zEnd ){
 28870  			if( zCur != zIn ){
 28871  				rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData);
 28872  			}
 28873  			break;
 28874  		}
 28875  		c = zCur[0];
 28876  		if( SAFE_HTTP(c) ){
 28877  			zCur++; continue;
 28878  		}
 28879  		if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){
 28880  			break;
 28881  		}		
 28882  		if( c == ' ' ){
 28883  			zOut[0] = '+';
 28884  			rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData);
 28885  		}else{
 28886  			zHex[1]	= "0123456789ABCDEF"[(c >> 4) & 0x0F];
 28887  			zHex[2] = "0123456789ABCDEF"[c & 0x0F];
 28888  			rc = xConsumer(zHex, sizeof(zHex), pUserData);
 28889  		}
 28890  		if( SXRET_OK != rc ){
 28891  			break;
 28892  		}				
 28893  		zIn = &zCur[1]; zCur = zIn ;
 28894  	}
 28895  	return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT;
 28896  }
 28897  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 28898  static sxi32 SyAsciiToHex(sxi32 c)
 28899  {
 28900  	if( c >= 'a' && c <= 'f' ){
 28901  		c += 10 - 'a';
 28902  		return c;
 28903  	}
 28904  	if( c >= '0' && c <= '9' ){
 28905  		c -= '0';
 28906  		return c;
 28907  	}
 28908  	if( c >= 'A' && c <= 'F') {
 28909  		c += 10 - 'A';
 28910  		return c;
 28911  	}		
 28912  	return 0; 
 28913  }
 28914  JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8)
 28915  {
 28916  	static const sxu8 Utf8Trans[] = {
 28917  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 28918  		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
 28919  		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
 28920  		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
 28921  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 28922  		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
 28923  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 28924  		0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
 28925  	};
 28926  	const char *zIn = zSrc;
 28927  	const char *zEnd;
 28928  	const char *zCur;
 28929  	sxu8 *zOutPtr;
 28930  	sxu8 zOut[10];
 28931  	sxi32 c, d;
 28932  	sxi32 rc;
 28933  #if defined(UNTRUST)
 28934  	if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
 28935  		return SXERR_EMPTY;
 28936  	}
 28937  #endif
 28938  	rc = SXRET_OK;
 28939  	zEnd = &zSrc[nLen];
 28940  	zCur = zIn;
 28941  	for(;;){
 28942  		while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){
 28943  			zCur++;
 28944  		}
 28945  		if( zCur != zIn ){
 28946  			/* Consume input */
 28947  			rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData);
 28948  			if( rc != SXRET_OK ){
 28949  				/* User consumer routine request an operation abort */
 28950  				break;
 28951  			}
 28952  		}
 28953  		if( zCur >= zEnd ){
 28954  			rc = SXRET_OK;
 28955  			break;
 28956  		}
 28957  		/* Decode unsafe HTTP characters */
 28958  		zOutPtr = zOut;
 28959  		if( zCur[0] == '+' ){
 28960  			*zOutPtr++ = ' ';
 28961  			zCur++;
 28962  		}else{
 28963  			if( &zCur[2] >= zEnd ){
 28964  				rc = SXERR_OVERFLOW;
 28965  				break;
 28966  			}
 28967  			c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
 28968  			zCur += 3;
 28969  			if( c < 0x000C0 ){
 28970  				*zOutPtr++ = (sxu8)c;
 28971  			}else{
 28972  				c = Utf8Trans[c-0xC0];
 28973  				while( zCur[0] == '%' ){
 28974  					d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
 28975  					if( (d&0xC0) != 0x80 ){
 28976  						break;
 28977  					}
 28978  					c = (c<<6) + (0x3f & d);
 28979  					zCur += 3;
 28980  				}
 28981  				if( bUTF8 == FALSE ){
 28982  					*zOutPtr++ = (sxu8)c;
 28983  				}else{
 28984  					SX_WRITE_UTF8(zOutPtr, c);
 28985  				}
 28986  			}
 28987  			
 28988  		}
 28989  		/* Consume the decoded characters */
 28990  		rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData);
 28991  		if( rc != SXRET_OK ){
 28992  			break;
 28993  		}
 28994  		/* Synchronize pointers */
 28995  		zIn = zCur;
 28996  	}
 28997  	return rc;
 28998  }
 28999  #ifndef JX9_DISABLE_BUILTIN_FUNC
 29000  static const char *zEngDay[] = { 
 29001  	"Sunday", "Monday", "Tuesday", "Wednesday", 
 29002  	"Thursday", "Friday", "Saturday"
 29003  };
 29004  static const char *zEngMonth[] = {
 29005  	"January", "February", "March", "April", 
 29006  	"May", "June", "July", "August", 
 29007  	"September", "October", "November", "December"
 29008  };
 29009  static const char * GetDay(sxi32 i)
 29010  {
 29011  	return zEngDay[ i % 7 ];
 29012  }
 29013  static const char * GetMonth(sxi32 i)
 29014  {
 29015  	return zEngMonth[ i % 12 ];
 29016  }
 29017  JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay)
 29018  {
 29019  	return GetDay(iDay);
 29020  }
 29021  JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth)
 29022  {
 29023  	return GetMonth(iMonth);
 29024  }
 29025  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 29026  /* SyRunTimeApi: sxfmt.c */
 29027  #define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
 29028  /*
 29029  ** Conversion types fall into various categories as defined by the
 29030  ** following enumeration.
 29031  */
 29032  #define SXFMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
 29033  #define SXFMT_FLOAT       2 /* Floating point.%f */
 29034  #define SXFMT_EXP         3 /* Exponentional notation.%e and %E */
 29035  #define SXFMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
 29036  #define SXFMT_SIZE        5 /* Total number of characters processed so far.%n */
 29037  #define SXFMT_STRING      6 /* Strings.%s */
 29038  #define SXFMT_PERCENT     7 /* Percent symbol.%% */
 29039  #define SXFMT_CHARX       8 /* Characters.%c */
 29040  #define SXFMT_ERROR       9 /* Used to indicate no such conversion type */
 29041  /* Extension by Symisc Systems */
 29042  #define SXFMT_RAWSTR     13 /* %z Pointer to raw string (SyString *) */
 29043  #define SXFMT_UNUSED     15 
 29044  /*
 29045  ** Allowed values for SyFmtInfo.flags
 29046  */
 29047  #define SXFLAG_SIGNED	0x01
 29048  #define SXFLAG_UNSIGNED 0x02
 29049  /* Allowed values for SyFmtConsumer.nType */
 29050  #define SXFMT_CONS_PROC		1	/* Consumer is a procedure */
 29051  #define SXFMT_CONS_STR		2	/* Consumer is a managed string */
 29052  #define SXFMT_CONS_FILE		5	/* Consumer is an open File */
 29053  #define SXFMT_CONS_BLOB		6	/* Consumer is a BLOB */
 29054  /*
 29055  ** Each builtin conversion character (ex: the 'd' in "%d") is described
 29056  ** by an instance of the following structure
 29057  */
 29058  typedef struct SyFmtInfo SyFmtInfo;
 29059  struct SyFmtInfo
 29060  {
 29061    char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
 29062    sxu8 base;     /* The base for radix conversion */
 29063    int flags;    /* One or more of SXFLAG_ constants below */
 29064    sxu8 type;     /* Conversion paradigm */
 29065    char *charset; /* The character set for conversion */
 29066    char *prefix;  /* Prefix on non-zero values in alt format */
 29067  };
 29068  typedef struct SyFmtConsumer SyFmtConsumer;
 29069  struct SyFmtConsumer
 29070  {
 29071  	sxu32 nLen; /* Total output length */
 29072  	sxi32 nType; /* Type of the consumer see below */
 29073  	sxi32 rc;	/* Consumer return value;Abort processing if rc != SXRET_OK */
 29074   union{
 29075  	struct{	
 29076  	ProcConsumer xUserConsumer;
 29077  	void *pUserData;
 29078  	}sFunc;  
 29079  	SyBlob *pBlob;
 29080   }uConsumer;	
 29081  }; 
 29082  #ifndef SX_OMIT_FLOATINGPOINT
 29083  static int getdigit(sxlongreal *val, int *cnt)
 29084  {
 29085    sxlongreal d;
 29086    int digit;
 29087  
 29088    if( (*cnt)++ >= 16 ){
 29089  	  return '0';
 29090    }
 29091    digit = (int)*val;
 29092    d = digit;
 29093     *val = (*val - d)*10.0;
 29094    return digit + '0' ;
 29095  }
 29096  #endif /* SX_OMIT_FLOATINGPOINT */
 29097  /*
 29098   * The following routine was taken from the SQLITE2 source tree and was
 29099   * extended by Symisc Systems to fit its need.
 29100   * Status: Public Domain
 29101   */
 29102  static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap)
 29103  {
 29104  	/*
 29105  	 * The following table is searched linearly, so it is good to put the most frequently
 29106  	 * used conversion types first.
 29107  	 */
 29108  static const SyFmtInfo aFmt[] = {
 29109    {  'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    }, 
 29110    {  's',  0, 0, SXFMT_STRING,     0,                  0    }, 
 29111    {  'c',  0, 0, SXFMT_CHARX,      0,                  0    }, 
 29112    {  'x', 16, 0, SXFMT_RADIX,      "0123456789abcdef", "x0" }, 
 29113    {  'X', 16, 0, SXFMT_RADIX,      "0123456789ABCDEF", "X0" }, 
 29114           /* -- Extensions by Symisc Systems -- */
 29115    {  'z',  0, 0, SXFMT_RAWSTR,     0,                   0   }, /* Pointer to a raw string (SyString *) */
 29116    {  'B',  2, 0, SXFMT_RADIX,      "01",                "b0"}, 
 29117           /* -- End of Extensions -- */
 29118    {  'o',  8, 0, SXFMT_RADIX,      "01234567",         "0"  }, 
 29119    {  'u', 10, 0, SXFMT_RADIX,      "0123456789",       0    }, 
 29120  #ifndef SX_OMIT_FLOATINGPOINT
 29121    {  'f',  0, SXFLAG_SIGNED, SXFMT_FLOAT,       0,     0    }, 
 29122    {  'e',  0, SXFLAG_SIGNED, SXFMT_EXP,        "e",    0    }, 
 29123    {  'E',  0, SXFLAG_SIGNED, SXFMT_EXP,        "E",    0    }, 
 29124    {  'g',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "e",    0    }, 
 29125    {  'G',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "E",    0    }, 
 29126  #endif
 29127    {  'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    }, 
 29128    {  'n',  0, 0, SXFMT_SIZE,       0,                  0    }, 
 29129    {  '%',  0, 0, SXFMT_PERCENT,    0,                  0    }, 
 29130    {  'p', 10, 0, SXFMT_RADIX,      "0123456789",       0    }
 29131  };
 29132    int c;                     /* Next character in the format string */
 29133    char *bufpt;               /* Pointer to the conversion buffer */
 29134    int precision;             /* Precision of the current field */
 29135    int length;                /* Length of the field */
 29136    int idx;                   /* A general purpose loop counter */
 29137    int width;                 /* Width of the current field */
 29138    sxu8 flag_leftjustify;   /* True if "-" flag is present */
 29139    sxu8 flag_plussign;      /* True if "+" flag is present */
 29140    sxu8 flag_blanksign;     /* True if " " flag is present */
 29141    sxu8 flag_alternateform; /* True if "#" flag is present */
 29142    sxu8 flag_zeropad;       /* True if field width constant starts with zero */
 29143    sxu8 flag_long;          /* True if "l" flag is present */
 29144    sxi64 longvalue;         /* Value for integer types */
 29145    const SyFmtInfo *infop;  /* Pointer to the appropriate info structure */
 29146    char buf[SXFMT_BUFSIZ];  /* Conversion buffer */
 29147    char prefix;             /* Prefix character."+" or "-" or " " or '\0'.*/
 29148    sxu8 errorflag = 0;      /* True if an error is encountered */
 29149    sxu8 xtype;              /* Conversion paradigm */
 29150    char *zExtra;    
 29151    static char spaces[] = "                                                  ";
 29152  #define etSPACESIZE ((int)sizeof(spaces)-1)
 29153  #ifndef SX_OMIT_FLOATINGPOINT
 29154    sxlongreal realvalue;    /* Value for real types */
 29155    int  exp;                /* exponent of real numbers */
 29156    double rounder;          /* Used for rounding floating point values */
 29157    sxu8 flag_dp;            /* True if decimal point should be shown */
 29158    sxu8 flag_rtz;           /* True if trailing zeros should be removed */
 29159    sxu8 flag_exp;           /* True to force display of the exponent */
 29160    int nsd;                 /* Number of significant digits returned */
 29161  #endif
 29162    int rc;
 29163  
 29164    length = 0;
 29165    bufpt = 0;
 29166    for(; (c=(*zFormat))!=0; ++zFormat){
 29167      if( c!='%' ){
 29168        unsigned int amt;
 29169        bufpt = (char *)zFormat;
 29170        amt = 1;
 29171        while( (c=(*++zFormat))!='%' && c!=0 ) amt++;
 29172  	  rc = xConsumer((const void *)bufpt, amt, pUserData);
 29173  	  if( rc != SXRET_OK ){
 29174  		  return SXERR_ABORT; /* Consumer routine request an operation abort */
 29175  	  }
 29176        if( c==0 ){
 29177  		  return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
 29178  	  }
 29179      }
 29180      if( (c=(*++zFormat))==0 ){
 29181        errorflag = 1;
 29182  	  rc = xConsumer("%", sizeof("%")-1, pUserData);
 29183  	  if( rc != SXRET_OK ){
 29184  		  return SXERR_ABORT; /* Consumer routine request an operation abort */
 29185  	  }
 29186        return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
 29187      }
 29188      /* Find out what flags are present */
 29189      flag_leftjustify = flag_plussign = flag_blanksign = 
 29190       flag_alternateform = flag_zeropad = 0;
 29191      do{
 29192        switch( c ){
 29193          case '-':   flag_leftjustify = 1;     c = 0;   break;
 29194          case '+':   flag_plussign = 1;        c = 0;   break;
 29195          case ' ':   flag_blanksign = 1;       c = 0;   break;
 29196          case '#':   flag_alternateform = 1;   c = 0;   break;
 29197          case '0':   flag_zeropad = 1;         c = 0;   break;
 29198          default:                                       break;
 29199        }
 29200      }while( c==0 && (c=(*++zFormat))!=0 );
 29201      /* Get the field width */
 29202      width = 0;
 29203      if( c=='*' ){
 29204        width = va_arg(ap, int);
 29205        if( width<0 ){
 29206          flag_leftjustify = 1;
 29207          width = -width;
 29208        }
 29209        c = *++zFormat;
 29210      }else{
 29211        while( c>='0' && c<='9' ){
 29212          width = width*10 + c - '0';
 29213          c = *++zFormat;
 29214        }
 29215      }
 29216      if( width > SXFMT_BUFSIZ-10 ){
 29217        width = SXFMT_BUFSIZ-10;
 29218      }
 29219      /* Get the precision */
 29220  	precision = -1;
 29221      if( c=='.' ){
 29222        precision = 0;
 29223        c = *++zFormat;
 29224        if( c=='*' ){
 29225          precision = va_arg(ap, int);
 29226          if( precision<0 ) precision = -precision;
 29227          c = *++zFormat;
 29228        }else{
 29229          while( c>='0' && c<='9' ){
 29230            precision = precision*10 + c - '0';
 29231            c = *++zFormat;
 29232          }
 29233        }
 29234      }
 29235      /* Get the conversion type modifier */
 29236  	flag_long = 0;
 29237      if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){
 29238        flag_long = (c == 'q') ? 2 : 1;
 29239        c = *++zFormat;
 29240  	  if( c == 'l' ){
 29241  		  /* Standard printf emulation 'lld' (expect a 64bit integer) */
 29242  		  flag_long = 2;
 29243  	  }
 29244      }
 29245      /* Fetch the info entry for the field */
 29246      infop = 0;
 29247      xtype = SXFMT_ERROR;
 29248  	for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
 29249        if( c==aFmt[idx].fmttype ){
 29250          infop = &aFmt[idx];
 29251  		xtype = infop->type;
 29252          break;
 29253        }
 29254      }
 29255      zExtra = 0;
 29256  
 29257      /*
 29258      ** At this point, variables are initialized as follows:
 29259      **
 29260      **   flag_alternateform          TRUE if a '#' is present.
 29261      **   flag_plussign               TRUE if a '+' is present.
 29262      **   flag_leftjustify            TRUE if a '-' is present or if the
 29263      **                               field width was negative.
 29264      **   flag_zeropad                TRUE if the width began with 0.
 29265      **   flag_long                   TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
 29266      **                               the conversion character.
 29267      **   flag_blanksign              TRUE if a ' ' is present.
 29268      **   width                       The specified field width.This is
 29269      **                               always non-negative.Zero is the default.
 29270      **   precision                   The specified precision.The default
 29271      **                               is -1.
 29272      **   xtype                       The object of the conversion.
 29273      **   infop                       Pointer to the appropriate info struct.
 29274      */
 29275      switch( xtype ){
 29276        case SXFMT_RADIX:
 29277          if( flag_long > 0 ){
 29278  			if( flag_long > 1 ){
 29279  				/* BSD quad: expect a 64-bit integer */
 29280  				longvalue = va_arg(ap, sxi64);
 29281  			}else{
 29282  				longvalue = va_arg(ap, sxlong);
 29283  			}
 29284  		}else{
 29285  			if( infop->flags & SXFLAG_SIGNED ){
 29286  				longvalue = va_arg(ap, sxi32);
 29287  			}else{
 29288  				longvalue = va_arg(ap, sxu32);
 29289  			}
 29290  		}
 29291  		/* Limit the precision to prevent overflowing buf[] during conversion */
 29292        if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
 29293  #if 1
 29294          /* For the format %#x, the value zero is printed "0" not "0x0".
 29295          ** I think this is stupid.*/
 29296          if( longvalue==0 ) flag_alternateform = 0;
 29297  #else
 29298          /* More sensible: turn off the prefix for octal (to prevent "00"), 
 29299          ** but leave the prefix for hex.*/
 29300          if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
 29301  #endif
 29302          if( infop->flags & SXFLAG_SIGNED ){
 29303            if( longvalue<0 ){ 
 29304              longvalue = -longvalue;
 29305  			/* Ticket 1433-003 */
 29306  			if( longvalue < 0 ){
 29307  				/* Overflow */
 29308  				longvalue= 0x7FFFFFFFFFFFFFFF;
 29309  			}
 29310              prefix = '-';
 29311            }else if( flag_plussign )  prefix = '+';
 29312            else if( flag_blanksign )  prefix = ' ';
 29313            else                       prefix = 0;
 29314          }else{
 29315  			if( longvalue<0 ){
 29316  				longvalue = -longvalue;
 29317  				/* Ticket 1433-003 */
 29318  				if( longvalue < 0 ){
 29319  					/* Overflow */
 29320  					longvalue= 0x7FFFFFFFFFFFFFFF;
 29321  				}
 29322  			}
 29323  			prefix = 0;
 29324  		}
 29325          if( flag_zeropad && precision<width-(prefix!=0) ){
 29326            precision = width-(prefix!=0);
 29327          }
 29328          bufpt = &buf[SXFMT_BUFSIZ-1];
 29329          {
 29330            register char *cset;      /* Use registers for speed */
 29331            register int base;
 29332            cset = infop->charset;
 29333            base = infop->base;
 29334            do{                                           /* Convert to ascii */
 29335              *(--bufpt) = cset[longvalue%base];
 29336              longvalue = longvalue/base;
 29337            }while( longvalue>0 );
 29338          }
 29339          length = &buf[SXFMT_BUFSIZ-1]-bufpt;
 29340          for(idx=precision-length; idx>0; idx--){
 29341            *(--bufpt) = '0';                             /* Zero pad */
 29342          }
 29343          if( prefix ) *(--bufpt) = prefix;               /* Add sign */
 29344          if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
 29345            char *pre, x;
 29346            pre = infop->prefix;
 29347            if( *bufpt!=pre[0] ){
 29348              for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
 29349            }
 29350          }
 29351          length = &buf[SXFMT_BUFSIZ-1]-bufpt;
 29352          break;
 29353        case SXFMT_FLOAT:
 29354        case SXFMT_EXP:
 29355        case SXFMT_GENERIC:
 29356  #ifndef SX_OMIT_FLOATINGPOINT
 29357  		realvalue = va_arg(ap, double);
 29358          if( precision<0 ) precision = 6;         /* Set default precision */
 29359          if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40;
 29360          if( realvalue<0.0 ){
 29361            realvalue = -realvalue;
 29362            prefix = '-';
 29363          }else{
 29364            if( flag_plussign )          prefix = '+';
 29365            else if( flag_blanksign )    prefix = ' ';
 29366            else                         prefix = 0;
 29367          }
 29368          if( infop->type==SXFMT_GENERIC && precision>0 ) precision--;
 29369          rounder = 0.0;
 29370  #if 0
 29371          /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
 29372          for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
 29373  #else
 29374          /* It makes more sense to use 0.5 */
 29375          for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
 29376  #endif
 29377          if( infop->type==SXFMT_FLOAT ) realvalue += rounder;
 29378          /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
 29379          exp = 0;
 29380          if( realvalue>0.0 ){
 29381            while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
 29382            while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
 29383            while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
 29384            while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
 29385            if( exp>350 || exp<-350 ){
 29386              bufpt = "NaN";
 29387              length = 3;
 29388              break;
 29389            }
 29390          }
 29391          bufpt = buf;
 29392          /*
 29393          ** If the field type is etGENERIC, then convert to either etEXP
 29394          ** or etFLOAT, as appropriate.
 29395          */
 29396          flag_exp = xtype==SXFMT_EXP;
 29397          if( xtype!=SXFMT_FLOAT ){
 29398            realvalue += rounder;
 29399            if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
 29400          }
 29401          if( xtype==SXFMT_GENERIC ){
 29402            flag_rtz = !flag_alternateform;
 29403            if( exp<-4 || exp>precision ){
 29404              xtype = SXFMT_EXP;
 29405            }else{
 29406              precision = precision - exp;
 29407              xtype = SXFMT_FLOAT;
 29408            }
 29409          }else{
 29410            flag_rtz = 0;
 29411          }
 29412          /*
 29413          ** The "exp+precision" test causes output to be of type etEXP if
 29414          ** the precision is too large to fit in buf[].
 29415          */
 29416          nsd = 0;
 29417          if( xtype==SXFMT_FLOAT && exp+precision<SXFMT_BUFSIZ-30 ){
 29418            flag_dp = (precision>0 || flag_alternateform);
 29419            if( prefix ) *(bufpt++) = prefix;         /* Sign */
 29420            if( exp<0 )  *(bufpt++) = '0';            /* Digits before "." */
 29421            else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
 29422            if( flag_dp ) *(bufpt++) = '.';           /* The decimal point */
 29423            for(exp++; exp<0 && precision>0; precision--, exp++){
 29424              *(bufpt++) = '0';
 29425            }
 29426            while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
 29427            *(bufpt--) = 0;                           /* Null terminate */
 29428            if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
 29429              while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
 29430              if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
 29431            }
 29432            bufpt++;                            /* point to next free slot */
 29433          }else{    /* etEXP or etGENERIC */
 29434            flag_dp = (precision>0 || flag_alternateform);
 29435            if( prefix ) *(bufpt++) = prefix;   /* Sign */
 29436            *(bufpt++) = (char)getdigit(&realvalue, &nsd);  /* First digit */
 29437            if( flag_dp ) *(bufpt++) = '.';     /* Decimal point */
 29438            while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
 29439            bufpt--;                            /* point to last digit */
 29440            if( flag_rtz && flag_dp ){          /* Remove tail zeros */
 29441              while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
 29442              if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
 29443            }
 29444            bufpt++;                            /* point to next free slot */
 29445            if( exp || flag_exp ){
 29446              *(bufpt++) = infop->charset[0];
 29447              if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
 29448              else       { *(bufpt++) = '+'; }
 29449              if( exp>=100 ){
 29450                *(bufpt++) = (char)((exp/100)+'0');                /* 100's digit */
 29451                exp %= 100;
 29452              }
 29453              *(bufpt++) = (char)(exp/10+'0');                     /* 10's digit */
 29454              *(bufpt++) = (char)(exp%10+'0');                     /* 1's digit */
 29455            }
 29456          }
 29457          /* The converted number is in buf[] and zero terminated.Output it.
 29458          ** Note that the number is in the usual order, not reversed as with
 29459          ** integer conversions.*/
 29460          length = bufpt-buf;
 29461          bufpt = buf;
 29462  
 29463          /* Special case:  Add leading zeros if the flag_zeropad flag is
 29464          ** set and we are not left justified */
 29465          if( flag_zeropad && !flag_leftjustify && length < width){
 29466            int i;
 29467            int nPad = width - length;
 29468            for(i=width; i>=nPad; i--){
 29469              bufpt[i] = bufpt[i-nPad];
 29470            }
 29471            i = prefix!=0;
 29472            while( nPad-- ) bufpt[i++] = '0';
 29473            length = width;
 29474          }
 29475  #else
 29476           bufpt = " ";
 29477  		 length = (int)sizeof(" ") - 1;
 29478  #endif /* SX_OMIT_FLOATINGPOINT */
 29479          break;
 29480        case SXFMT_SIZE:{
 29481  		 int *pSize = va_arg(ap, int *);
 29482  		 *pSize = ((SyFmtConsumer *)pUserData)->nLen;
 29483  		 length = width = 0;
 29484  					  }
 29485          break;
 29486        case SXFMT_PERCENT:
 29487          buf[0] = '%';
 29488          bufpt = buf;
 29489          length = 1;
 29490          break;
 29491        case SXFMT_CHARX:
 29492          c = va_arg(ap, int);
 29493  		buf[0] = (char)c;
 29494  		/* Limit the precision to prevent overflowing buf[] during conversion */
 29495  		if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
 29496          if( precision>=0 ){
 29497            for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
 29498            length = precision;
 29499          }else{
 29500            length =1;
 29501          }
 29502          bufpt = buf;
 29503          break;
 29504        case SXFMT_STRING:
 29505          bufpt = va_arg(ap, char*);
 29506          if( bufpt==0 ){
 29507            bufpt = " ";
 29508  		  length = (int)sizeof(" ")-1;
 29509  		  break;
 29510          }
 29511  		length = precision;
 29512  		if( precision < 0 ){
 29513  			/* Symisc extension */
 29514  			length = (int)SyStrlen(bufpt);
 29515  		}
 29516          if( precision>=0 && precision<length ) length = precision;
 29517          break;
 29518  	case SXFMT_RAWSTR:{
 29519  		/* Symisc extension */
 29520  		SyString *pStr = va_arg(ap, SyString *);
 29521  		if( pStr == 0 || pStr->zString == 0 ){
 29522  			 bufpt = " ";
 29523  		     length = (int)sizeof(char);
 29524  		     break;
 29525  		}
 29526  		bufpt = (char *)pStr->zString;
 29527  		length = (int)pStr->nByte;
 29528  		break;
 29529  					  }
 29530        case SXFMT_ERROR:
 29531          buf[0] = '?';
 29532          bufpt = buf;
 29533  		length = (int)sizeof(char);
 29534          if( c==0 ) zFormat--;
 29535          break;
 29536      }/* End switch over the format type */
 29537      /*
 29538      ** The text of the conversion is pointed to by "bufpt" and is
 29539      ** "length" characters long.The field width is "width".Do
 29540      ** the output.
 29541      */
 29542      if( !flag_leftjustify ){
 29543        register int nspace;
 29544        nspace = width-length;
 29545        if( nspace>0 ){
 29546          while( nspace>=etSPACESIZE ){
 29547  			rc = xConsumer(spaces, etSPACESIZE, pUserData);
 29548  			if( rc != SXRET_OK ){
 29549  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 29550  			}
 29551  			nspace -= etSPACESIZE;
 29552          }
 29553          if( nspace>0 ){
 29554  			rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
 29555  			if( rc != SXRET_OK ){
 29556  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 29557  			}
 29558  		}
 29559        }
 29560      }
 29561      if( length>0 ){
 29562  		rc = xConsumer(bufpt, (unsigned int)length, pUserData);
 29563  		if( rc != SXRET_OK ){
 29564  		  return SXERR_ABORT; /* Consumer routine request an operation abort */
 29565  		}
 29566      }
 29567      if( flag_leftjustify ){
 29568        register int nspace;
 29569        nspace = width-length;
 29570        if( nspace>0 ){
 29571          while( nspace>=etSPACESIZE ){
 29572  			rc = xConsumer(spaces, etSPACESIZE, pUserData);
 29573  			if( rc != SXRET_OK ){
 29574  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 29575  			}
 29576  			nspace -= etSPACESIZE;
 29577          }
 29578          if( nspace>0 ){
 29579  			rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
 29580  			if( rc != SXRET_OK ){
 29581  				return SXERR_ABORT; /* Consumer routine request an operation abort */
 29582  			}
 29583  		}
 29584        }
 29585      }
 29586    }/* End for loop over the format string */
 29587    return errorflag ? SXERR_FORMAT : SXRET_OK;
 29588  } 
 29589  static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData)
 29590  {
 29591  	SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
 29592  	sxi32 rc = SXERR_ABORT;
 29593  	switch(pConsumer->nType){
 29594  	case SXFMT_CONS_PROC:
 29595  			/* User callback */
 29596  			rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
 29597  			break;
 29598  	case SXFMT_CONS_BLOB:
 29599  			/* Blob consumer */
 29600  			rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
 29601  			break;
 29602  		default: 
 29603  			/* Unknown consumer */
 29604  			break;
 29605  	}
 29606  	/* Update total number of bytes consumed so far */
 29607  	pConsumer->nLen += nLen;
 29608  	pConsumer->rc = rc;
 29609  	return rc;	
 29610  }
 29611  static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap)
 29612  {
 29613  	SyFmtConsumer sCons;
 29614  	sCons.nType = nType;
 29615  	sCons.rc = SXRET_OK;
 29616  	sCons.nLen = 0;
 29617  	if( pOutLen ){
 29618  		*pOutLen = 0;
 29619  	}
 29620  	switch(nType){
 29621  	case SXFMT_CONS_PROC:
 29622  #if defined(UNTRUST)
 29623  			if( xUserCons == 0 ){
 29624  				return SXERR_EMPTY;
 29625  			}
 29626  #endif
 29627  			sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
 29628  			sCons.uConsumer.sFunc.pUserData	    = pUserData;
 29629  		break;
 29630  		case SXFMT_CONS_BLOB:
 29631  			sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
 29632  			break;
 29633  		default: 
 29634  			return SXERR_UNKNOWN;
 29635  	}
 29636  	InternFormat(FormatConsumer, &sCons, zFormat, ap); 
 29637  	if( pOutLen ){
 29638  		*pOutLen = sCons.nLen;
 29639  	}
 29640  	return sCons.rc;
 29641  }
 29642  JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...)
 29643  {
 29644  	va_list ap;
 29645  	sxi32 rc;
 29646  #if defined(UNTRUST)	
 29647  	if( SX_EMPTY_STR(zFormat) ){
 29648  		return SXERR_EMPTY;
 29649  	}
 29650  #endif
 29651  	va_start(ap, zFormat);
 29652  	rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
 29653  	va_end(ap);
 29654  	return rc;
 29655  }
 29656  JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...)
 29657  {
 29658  	va_list ap;
 29659  	sxu32 n;
 29660  #if defined(UNTRUST)	
 29661  	if( SX_EMPTY_STR(zFormat) ){
 29662  		return 0;
 29663  	}
 29664  #endif			
 29665  	va_start(ap, zFormat);
 29666  	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
 29667  	va_end(ap);
 29668  	return n;
 29669  }
 29670  JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap)
 29671  {
 29672  	sxu32 n = 0; /* cc warning */
 29673  #if defined(UNTRUST)	
 29674  	if( SX_EMPTY_STR(zFormat) ){
 29675  		return 0;
 29676  	}
 29677  #endif	
 29678  	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
 29679  	return n;
 29680  }
 29681  JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...)
 29682  {
 29683  	SyBlob sBlob;
 29684  	va_list ap;
 29685  	sxu32 n;
 29686  #if defined(UNTRUST)	
 29687  	if( SX_EMPTY_STR(zFormat) ){
 29688  		return 0;
 29689  	}
 29690  #endif	
 29691  	if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){
 29692  		return 0;
 29693  	}		
 29694  	va_start(ap, zFormat);
 29695  	FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
 29696  	va_end(ap);
 29697  	n = SyBlobLength(&sBlob);
 29698  	/* Append the null terminator */
 29699  	sBlob.mByte++;
 29700  	SyBlobAppend(&sBlob, "\0", sizeof(char));
 29701  	return n;
 29702  }
 29703  #ifndef JX9_DISABLE_BUILTIN_FUNC
 29704  /*
 29705   * Zip File Format:
 29706   *
 29707   * Byte order: Little-endian
 29708   * 
 29709   * [Local file header + Compressed data [+ Extended local header]?]*
 29710   * [Central directory]*
 29711   * [End of central directory record]
 29712   * 
 29713   * Local file header:*
 29714   * Offset   Length   Contents
 29715   *  0      4 bytes  Local file header signature (0x04034b50)
 29716   *  4      2 bytes  Version needed to extract
 29717   *  6      2 bytes  General purpose bit flag
 29718   *  8      2 bytes  Compression method
 29719   * 10      2 bytes  Last mod file time
 29720   * 12      2 bytes  Last mod file date
 29721   * 14      4 bytes  CRC-32
 29722   * 18      4 bytes  Compressed size (n)
 29723   * 22      4 bytes  Uncompressed size
 29724   * 26      2 bytes  Filename length (f)
 29725   * 28      2 bytes  Extra field length (e)
 29726   * 30     (f)bytes  Filename
 29727   *        (e)bytes  Extra field
 29728   *        (n)bytes  Compressed data
 29729   *
 29730   * Extended local header:*
 29731   * Offset   Length   Contents
 29732   *  0      4 bytes  Extended Local file header signature (0x08074b50)
 29733   *  4      4 bytes  CRC-32
 29734   *  8      4 bytes  Compressed size
 29735   * 12      4 bytes  Uncompressed size
 29736   *
 29737   * Extra field:?(if any)
 29738   * Offset 	Length		Contents
 29739   * 0	  	2 bytes		Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip
 29740   * 2	  	2 bytes		Data size (g)
 29741   * 		  	(g) bytes	(g) bytes of extra field
 29742   * 
 29743   * Central directory:*
 29744   * Offset   Length   Contents
 29745   *  0      4 bytes  Central file header signature (0x02014b50)
 29746   *  4      2 bytes  Version made by
 29747   *  6      2 bytes  Version needed to extract
 29748   *  8      2 bytes  General purpose bit flag
 29749   * 10      2 bytes  Compression method
 29750   * 12      2 bytes  Last mod file time
 29751   * 14      2 bytes  Last mod file date
 29752   * 16      4 bytes  CRC-32
 29753   * 20      4 bytes  Compressed size
 29754   * 24      4 bytes  Uncompressed size
 29755   * 28      2 bytes  Filename length (f)
 29756   * 30      2 bytes  Extra field length (e)
 29757   * 32      2 bytes  File comment length (c)
 29758   * 34      2 bytes  Disk number start
 29759   * 36      2 bytes  Internal file attributes
 29760   * 38      4 bytes  External file attributes
 29761   * 42      4 bytes  Relative offset of local header
 29762   * 46     (f)bytes  Filename
 29763   *        (e)bytes  Extra field
 29764   *        (c)bytes  File comment
 29765   *
 29766   * End of central directory record:
 29767   * Offset   Length   Contents
 29768   *  0      4 bytes  End of central dir signature (0x06054b50)
 29769   *  4      2 bytes  Number of this disk
 29770   *  6      2 bytes  Number of the disk with the start of the central directory
 29771   *  8      2 bytes  Total number of entries in the central dir on this disk
 29772   * 10      2 bytes  Total number of entries in the central dir
 29773   * 12      4 bytes  Size of the central directory
 29774   * 16      4 bytes  Offset of start of central directory with respect to the starting disk number
 29775   * 20      2 bytes  zipfile comment length (c)
 29776   * 22     (c)bytes  zipfile comment
 29777   *
 29778   * compression method: (2 bytes)
 29779   *          0 - The file is stored (no compression)
 29780   *          1 - The file is Shrunk
 29781   *          2 - The file is Reduced with compression factor 1
 29782   *          3 - The file is Reduced with compression factor 2
 29783   *          4 - The file is Reduced with compression factor 3
 29784   *          5 - The file is Reduced with compression factor 4
 29785   *          6 - The file is Imploded
 29786   *          7 - Reserved for Tokenizing compression algorithm
 29787   *          8 - The file is Deflated
 29788   */ 
 29789  
 29790  #define SXMAKE_ZIP_WORKBUF	(SXU16_HIGH/2)	/* 32KB Initial working buffer size */
 29791  #define SXMAKE_ZIP_EXTRACT_VER	0x000a	/* Version needed to extract */
 29792  #define SXMAKE_ZIP_VER	0x003	/* Version made by */
 29793  
 29794  #define SXZIP_CENTRAL_MAGIC			0x02014b50
 29795  #define SXZIP_END_CENTRAL_MAGIC		0x06054b50
 29796  #define SXZIP_LOCAL_MAGIC			0x04034b50
 29797  /*#define SXZIP_CRC32_START			0xdebb20e3*/
 29798  
 29799  #define SXZIP_LOCAL_HDRSZ		30	/* Local header size */
 29800  #define SXZIP_LOCAL_EXT_HDRZ	16	/* Extended local header(footer) size */
 29801  #define SXZIP_CENTRAL_HDRSZ		46	/* Central directory header size */
 29802  #define SXZIP_END_CENTRAL_HDRSZ	22	/* End of central directory header size */
 29803  	 
 29804  #define SXARCHIVE_HASH_SIZE	64 /* Starting hash table size(MUST BE POWER OF 2)*/
 29805  static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len)
 29806  {
 29807  	if( Len < sizeof(sxu32) ){ 
 29808  		return SXERR_SHORT;
 29809  	}
 29810  	*uNB =  buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
 29811  	return SXRET_OK;
 29812  }
 29813  static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen)
 29814  {
 29815  	if( nLen < sizeof(sxu16) ){
 29816  		return SXERR_SHORT;
 29817  	}
 29818  	*pOut = zBuf[0] + (zBuf[1] <<8);
 29819  	
 29820  	return SXRET_OK;
 29821  }
 29822  /*
 29823   * Archive hashtable manager
 29824   */
 29825  static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry)
 29826  {
 29827  	SyArchiveEntry *pBucketEntry;
 29828  	SyString sEntry;
 29829  	sxu32 nHash;
 29830  
 29831  	nHash = pArch->xHash(zName, nLen);
 29832  	pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)];
 29833  
 29834  	SyStringInitFromBuf(&sEntry, zName, nLen);
 29835  
 29836  	for(;;){
 29837  		if( pBucketEntry == 0 ){
 29838  			break;
 29839  		}
 29840  		if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){
 29841  			if( ppEntry ){
 29842  				*ppEntry = pBucketEntry;
 29843  			}
 29844  			return SXRET_OK;
 29845  		}
 29846  		pBucketEntry = pBucketEntry->pNextHash;
 29847  	}
 29848  	return SXERR_NOTFOUND;
 29849  }
 29850  static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry)
 29851  {
 29852  	pEntry->pNextHash = apTable[nBucket];
 29853  	if( apTable[nBucket] != 0 ){
 29854  		apTable[nBucket]->pPrevHash = pEntry;
 29855  	}
 29856  	apTable[nBucket] = pEntry;
 29857  }
 29858  static sxi32 ArchiveHashGrowTable(SyArchive *pArch)
 29859  {
 29860  	sxu32 nNewSize = pArch->nSize * 2;
 29861  	SyArchiveEntry **apNew;
 29862  	SyArchiveEntry *pEntry;
 29863  	sxu32 n;
 29864  
 29865  	/* Allocate a new table */
 29866  	apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *));
 29867  	if( apNew == 0 ){
 29868  		return SXRET_OK; /* Not so fatal, simply a performance hit */
 29869  	}
 29870  	SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *));
 29871  	/* Rehash old entries */
 29872  	for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){
 29873  		pEntry->pNextHash = pEntry->pPrevHash = 0;
 29874  		ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry);
 29875  	}
 29876  	/* Release the old table */
 29877  	SyMemBackendFree(pArch->pAllocator, pArch->apHash);
 29878  	pArch->apHash = apNew;
 29879  	pArch->nSize = nNewSize;
 29880  
 29881  	return SXRET_OK;
 29882  }
 29883  static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry)
 29884  {
 29885  	if( pArch->nLoaded > pArch->nSize * 3 ){
 29886  		ArchiveHashGrowTable(&(*pArch));
 29887  	}
 29888  	pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName));
 29889  	/* Install the entry in its bucket */
 29890  	ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry);
 29891  	MACRO_LD_PUSH(pArch->pList, pEntry);
 29892  	pArch->nLoaded++;
 29893  
 29894  	return SXRET_OK;
 29895  }
 29896   /*
 29897    * Parse the End of central directory and report status
 29898    */ 
 29899   static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf)
 29900   {
 29901  	sxu32 nMagic = 0; /* cc -O6 warning */
 29902   	sxi32 rc;
 29903   	
 29904   	/* Sanity check */
 29905   	rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32));
 29906   	if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){
 29907   		return SXERR_CORRUPT;
 29908   	}
 29909   	/* # of entries */
 29910   	rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16));
 29911   	if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){
 29912   		return SXERR_CORRUPT;
 29913   	}
 29914   	/* Size of central directory */
 29915   	rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32));
 29916   	if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
 29917   		return SXERR_CORRUPT;
 29918   	}
 29919   	/* Starting offset of central directory */
 29920   	rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32));
 29921   	if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
 29922   		return SXERR_CORRUPT;
 29923   	}
 29924   	
 29925   	return SXRET_OK;
 29926   }
 29927   /*
 29928    * Fill the zip entry with the appropriate information from the central directory
 29929    */
 29930  static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset)
 29931   { 
 29932   	SyString *pName = &pEntry->sFileName; /* File name */
 29933   	sxu16 nDosDate, nDosTime;
 29934  	sxu16 nComment = 0 ;
 29935  	sxu32 nMagic = 0; /* cc -O6 warning */
 29936  	sxi32 rc; 	
 29937  	nDosDate = nDosTime = 0; /* cc -O6 warning */
 29938  	SXUNUSED(pArch);
 29939   	/* Sanity check */
 29940   	rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32));
 29941   	if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){
 29942   		rc = SXERR_CORRUPT; 		
 29943   		/*
 29944   		 * Try to recover by examing the next central directory record.
 29945   		 * Dont worry here, there is no risk of an infinite loop since
 29946  		 * the buffer size is delimited.
 29947   		 */
 29948  
 29949   		/* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */
 29950   		goto update;
 29951   	}
 29952   	/*
 29953   	 * entry name length
 29954   	 */
 29955   	SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16));
 29956   	if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){
 29957   		 rc = SXERR_BIG;
 29958   		 goto update;
 29959   	}
 29960   	/* Extra information */
 29961   	SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16));
 29962   	/* Comment length  */
 29963   	SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16)); 	
 29964   	/* Compression method 0 == stored / 8 == deflated */
 29965   	rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16));
 29966   	/* DOS Timestamp */
 29967   	SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16));
 29968   	SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16));
 29969   	SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt);
 29970  	/* Little hack to fix month index  */
 29971  	pEntry->sFmt.tm_mon--;
 29972   	/* CRC32 */
 29973   	rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32));
 29974   	/* Content size before compression */
 29975   	rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32));
 29976   	if(  pEntry->nByte > SXI32_HIGH ){
 29977   		rc = SXERR_BIG;
 29978   		goto update; 
 29979   	} 	
 29980   	/*
 29981   	 * Content size after compression.
 29982   	 * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr
 29983   	 */ 
 29984   	rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32));
 29985   	if( pEntry->nByteCompr > SXI32_HIGH ){
 29986   		rc = SXERR_BIG;
 29987   		goto update; 
 29988   	} 	 	
 29989   	/* Finally grab the contents offset */
 29990   	SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32));
 29991   	if( pEntry->nOfft > SXI32_HIGH ){
 29992   		rc = SXERR_BIG;
 29993   		goto update;
 29994   	} 	
 29995    	 rc = SXRET_OK;
 29996  update:	  
 29997   	/* Update the offset to point to the next central directory record */
 29998   	*pNextOffset =  SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment;
 29999   	return rc; /* Report failure or success */
 30000  }
 30001  static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc)
 30002  {	
 30003  	sxu16 nExtra, nNameLen;
 30004  	unsigned char *zHdr;
 30005  	nExtra = nNameLen = 0;
 30006  	zHdr = (unsigned char *)pSrc;
 30007  	zHdr = &zHdr[pEntry->nOfft];
 30008  	if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){
 30009  		return SXERR_CORRUPT;
 30010  	}
 30011  	SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16));
 30012  	SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16));
 30013  	/* Fix contents offset */
 30014  	pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen;
 30015  	return SXRET_OK;
 30016  }
 30017  /*
 30018   * Extract all valid entries from the central directory 
 30019   */	 
 30020  static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc)
 30021  {
 30022  	SyArchiveEntry *pEntry, *pDup;
 30023  	const unsigned char *zEnd ; /* End of central directory */
 30024  	sxu32 nIncr, nOfft;          /* Central Offset */
 30025  	SyString *pName;	        /* Entry name */
 30026  	char *zName;
 30027  	sxi32 rc;
 30028  	
 30029  	nOfft = nIncr = 0;
 30030  	zEnd = &zCentral[nLen];
 30031  	
 30032  	for(;;){
 30033  		if( &zCentral[nOfft] >= zEnd ){
 30034  			break;
 30035  		}
 30036  		/* Add a new entry */
 30037  		pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry));
 30038  		if( pEntry == 0 ){
 30039  			break;
 30040  		}
 30041  		SyZero(pEntry, sizeof(SyArchiveEntry)); 
 30042  		pEntry->nMagic = SXARCH_MAGIC;
 30043  		nIncr = 0;
 30044  		rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr);
 30045  		if( rc == SXRET_OK ){
 30046  			/* Fix the starting record offset so we can access entry contents correctly */
 30047  			rc = ZipFixOffset(pEntry, pSrc);
 30048  		}
 30049  		if(rc != SXRET_OK ){
 30050  			sxu32 nJmp = 0;
 30051  			SyMemBackendPoolFree(pArch->pAllocator, pEntry);
 30052  			/* Try to recover by brute-forcing for a valid central directory record */
 30053  			if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]), 
 30054  				(const void *)"PK\001\002", sizeof(sxu32), &nJmp)){
 30055  					nOfft += nIncr + nJmp; /* Check next entry */
 30056  					continue;
 30057  			}
 30058  			break; /* Giving up, archive is hopelessly corrupted */
 30059  		}
 30060  		pName = &pEntry->sFileName;
 30061  		pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ];
 30062  		if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){
 30063  			/* Ignore zero length records (except folders) and records without names */
 30064  			SyMemBackendPoolFree(pArch->pAllocator, pEntry); 
 30065  		 	nOfft += nIncr; /* Check next entry */
 30066  			continue;
 30067  		}
 30068  		zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte);
 30069   	 	if( zName == 0 ){
 30070   	 		 SyMemBackendPoolFree(pArch->pAllocator, pEntry); 
 30071  		 	 nOfft += nIncr; /* Check next entry */
 30072  			continue;
 30073   	 	}
 30074  		pName->zString = (const char *)zName;
 30075  		/* Check for duplicates */
 30076  		rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup);
 30077  		if( rc == SXRET_OK ){
 30078  			/* Another entry with the same name exists ; link them together */
 30079  			pEntry->pNextName = pDup->pNextName;
 30080  			pDup->pNextName = pEntry;
 30081  			pDup->nDup++;
 30082  		}else{
 30083  			/* Insert in hashtable */
 30084  			ArchiveHashInstallEntry(pArch, pEntry);
 30085  		}	
 30086  		nOfft += nIncr;	/* Check next record */
 30087  	}
 30088  	pArch->pCursor = pArch->pList;
 30089  	
 30090  	return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY;
 30091  } 						
 30092  JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen)
 30093   {
 30094   	const unsigned char *zCentral, *zEnd;
 30095   	sxi32 rc;
 30096  #if defined(UNTRUST)
 30097   	if( SXARCH_INVALID(pArch) || zBuf == 0 ){
 30098   		return SXERR_INVALID;
 30099   	}
 30100  #endif 	
 30101   	/* The miminal size of a zip archive:
 30102   	 * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ
 30103   	 * 		30				46				22
 30104   	 */
 30105   	 if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){
 30106   	 	return SXERR_CORRUPT; /* Don't bother processing return immediately */
 30107   	 }
 30108   	  		
 30109   	zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ];
 30110   	/* Find the end of central directory */
 30111   	while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) &&
 30112  		zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){
 30113   		zEnd--;
 30114   	} 	
 30115   	/* Parse the end of central directory */
 30116   	rc = ParseEndOfCentralDirectory(&(*pArch), zEnd);
 30117   	if( rc != SXRET_OK ){
 30118   		return rc;
 30119   	} 	
 30120   	
 30121   	/* Find the starting offset of the central directory */
 30122   	zCentral = &zEnd[-(sxi32)pArch->nCentralSize];
 30123   	if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
 30124   		if( pArch->nCentralOfft >= nLen ){
 30125  			/* Corrupted central directory offset */
 30126   			return SXERR_CORRUPT;
 30127   		}
 30128   		zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft];
 30129   		if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
 30130   			/* Corrupted zip archive */
 30131   			return SXERR_CORRUPT;
 30132   		}
 30133   		/* Fall thru and extract all valid entries from the central directory */
 30134   	}
 30135   	rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf);
 30136   	return rc;
 30137   }
 30138  /*
 30139    * Default comparison function.
 30140    */
 30141   static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2)
 30142   {
 30143  	 sxi32 rc;
 30144  	 rc = SyStringCmp(pStr1, pStr2, SyMemcmp);
 30145  	 return rc;
 30146   }
 30147  JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp)
 30148   {
 30149  	SyArchiveEntry **apHash;
 30150  #if defined(UNTRUST)
 30151   	if( pArch == 0 ){
 30152   		return SXERR_EMPTY;
 30153   	}
 30154  #endif
 30155   	SyZero(pArch, sizeof(SyArchive));
 30156   	/* Allocate a new hashtable */ 	
 30157  	apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
 30158  	if( apHash == 0){
 30159  		return SXERR_MEM;
 30160  	}
 30161  	SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
 30162  	pArch->apHash = apHash;
 30163  	pArch->xHash  = xHash ? xHash : SyBinHash;
 30164  	pArch->xCmp   = xCmp ? xCmp : ArchiveHashCmp;
 30165  	pArch->nSize  = SXARCHIVE_HASH_SIZE;
 30166   	pArch->pAllocator = &(*pAllocator);
 30167   	pArch->nMagic = SXARCH_MAGIC;
 30168   	return SXRET_OK;
 30169   }
 30170   static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry)
 30171   {
 30172   	SyArchiveEntry *pDup = pEntry->pNextName;
 30173   	SyArchiveEntry *pNextDup;
 30174   	
 30175   	/* Release duplicates first since there are not stored in the hashtable */
 30176   	for(;;){
 30177   		if( pEntry->nDup == 0 ){
 30178   			break;
 30179   		}
 30180   		pNextDup = pDup->pNextName;
 30181  		pDup->nMagic = 0x2661;
 30182   		SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName));
 30183   		SyMemBackendPoolFree(pAllocator, pDup); 		
 30184   		pDup = pNextDup;
 30185   		pEntry->nDup--;
 30186   	} 		
 30187  	pEntry->nMagic = 0x2661;
 30188    	SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName));
 30189   	SyMemBackendPoolFree(pAllocator, pEntry);
 30190   	return SXRET_OK;
 30191   } 	
 30192  JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch)
 30193   {
 30194  	SyArchiveEntry *pEntry, *pNext;
 30195   	pEntry = pArch->pList; 	 	
 30196  	for(;;){
 30197  		if( pArch->nLoaded < 1 ){
 30198  			break;
 30199  		}
 30200  		pNext = pEntry->pNext;
 30201  		MACRO_LD_REMOVE(pArch->pList, pEntry);
 30202  		ArchiveReleaseEntry(pArch->pAllocator, pEntry);
 30203  		pEntry = pNext;
 30204  		pArch->nLoaded--;
 30205  	}
 30206  	SyMemBackendFree(pArch->pAllocator, pArch->apHash);
 30207  	pArch->pCursor = 0;
 30208  	pArch->nMagic = 0x2626;
 30209  	return SXRET_OK;
 30210   }
 30211   JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch)
 30212   {
 30213  	pArch->pCursor = pArch->pList;
 30214  	return SXRET_OK;
 30215   }
 30216   JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry)
 30217   {
 30218  	SyArchiveEntry *pNext;
 30219  	if( pArch->pCursor == 0 ){
 30220  		/* Rewind the cursor */
 30221  		pArch->pCursor = pArch->pList;
 30222  		return SXERR_EOF;
 30223  	}
 30224  	*ppEntry = pArch->pCursor;
 30225  	 pNext = pArch->pCursor->pNext;
 30226  	 /* Advance the cursor to the next entry */
 30227  	 pArch->pCursor = pNext;
 30228  	 return SXRET_OK;
 30229    }
 30230  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 30231  /*
 30232   * Psuedo Random Number Generator (PRNG)
 30233   * @authors: SQLite authors <http://www.sqlite.org/>
 30234   * @status: Public Domain
 30235   * NOTE:
 30236   *  Nothing in this file or anywhere else in the library does any kind of
 30237   *  encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
 30238   *  number generator) not as an encryption device.
 30239   */
 30240  #define SXPRNG_MAGIC	0x13C4
 30241  #ifdef __UNIXES__
 30242  #include <sys/types.h>
 30243  #include <sys/stat.h>
 30244  #include <fcntl.h>
 30245  #include <unistd.h>
 30246  #include <errno.h>
 30247  #include <time.h>
 30248  #include <sys/time.h>
 30249  #endif
 30250  static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused)
 30251  {
 30252  	char *zBuf = (char *)pBuf;
 30253  #ifdef __WINNT__
 30254  	DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */
 30255  #elif defined(__UNIXES__)
 30256  	pid_t pid;
 30257  	int fd;
 30258  #else
 30259  	char zGarbage[128]; /* Yes, keep this buffer uninitialized */
 30260  #endif
 30261  	SXUNUSED(pUnused);
 30262  #ifdef __WINNT__
 30263  #ifndef __MINGW32__
 30264  	nProcessID = GetProcessId(GetCurrentProcess());
 30265  #endif
 30266  	SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
 30267  	if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME)  ){
 30268  		GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
 30269  	}
 30270  #elif defined(__UNIXES__)
 30271  	fd = open("/dev/urandom", O_RDONLY);
 30272  	if (fd >= 0 ){
 30273  		if( read(fd, zBuf, nLen) > 0 ){
 30274  			return SXRET_OK;
 30275  		}
 30276  		/* FALL THRU */
 30277  	}
 30278  	pid = getpid();
 30279  	SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
 30280  	if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval)  ){
 30281  		gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
 30282  	}
 30283  #else
 30284  	/* Fill with uninitialized data */
 30285  	SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
 30286  #endif
 30287  	return SXRET_OK;
 30288  }
 30289  JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData)
 30290  {
 30291  	char zSeed[256];
 30292  	sxu8 t;
 30293  	sxi32 rc;
 30294  	sxu32 i;
 30295  	if( pCtx->nMagic == SXPRNG_MAGIC ){
 30296  		return SXRET_OK; /* Already initialized */
 30297  	}
 30298   /* Initialize the state of the random number generator once, 
 30299    ** the first time this routine is called.The seed value does
 30300    ** not need to contain a lot of randomness since we are not
 30301    ** trying to do secure encryption or anything like that...
 30302    */	
 30303  	if( xSeed == 0 ){
 30304  		xSeed = SyOSUtilRandomSeed;
 30305  	}
 30306  	rc = xSeed(zSeed, sizeof(zSeed), pUserData);
 30307  	if( rc != SXRET_OK ){
 30308  		return rc;
 30309  	}
 30310  	pCtx->i = pCtx->j = 0;
 30311  	for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){
 30312  		pCtx->s[i] = (unsigned char)i;
 30313      }
 30314      for(i=0; i < sizeof(zSeed) ; i++){
 30315        pCtx->j += pCtx->s[i] + zSeed[i];
 30316        t = pCtx->s[pCtx->j];
 30317        pCtx->s[pCtx->j] = pCtx->s[i];
 30318        pCtx->s[i] = t;
 30319      }
 30320  	pCtx->nMagic = SXPRNG_MAGIC;
 30321  	
 30322  	return SXRET_OK;
 30323  }
 30324  /*
 30325   * Get a single 8-bit random value using the RC4 PRNG.
 30326   */
 30327  static sxu8 randomByte(SyPRNGCtx *pCtx)
 30328  {
 30329    sxu8 t;
 30330    
 30331    /* Generate and return single random byte */
 30332    pCtx->i++;
 30333    t = pCtx->s[pCtx->i];
 30334    pCtx->j += t;
 30335    pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
 30336    pCtx->s[pCtx->j] = t;
 30337    t += pCtx->s[pCtx->i];
 30338    return pCtx->s[t];
 30339  }
 30340  JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen)
 30341  {
 30342  	unsigned char *zBuf = (unsigned char *)pBuf;
 30343  	unsigned char *zEnd = &zBuf[nLen];
 30344  #if defined(UNTRUST)
 30345  	if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){
 30346  		return SXERR_EMPTY;
 30347  	}
 30348  #endif
 30349  	if(pCtx->nMagic != SXPRNG_MAGIC ){
 30350  		return SXERR_CORRUPT;
 30351  	}
 30352  	for(;;){
 30353  		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;	
 30354  		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;	
 30355  		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;	
 30356  		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;	
 30357  	}
 30358  	return SXRET_OK;  
 30359  }
 30360  #ifndef JX9_DISABLE_BUILTIN_FUNC
 30361  #ifndef JX9_DISABLE_HASH_FUNC
 30362  /* SyRunTimeApi: sxhash.c */
 30363  /*
 30364   * This code implements the MD5 message-digest algorithm.
 30365   * The algorithm is due to Ron Rivest.This code was
 30366   * written by Colin Plumb in 1993, no copyright is claimed.
 30367   * This code is in the public domain; do with it what you wish.
 30368   *
 30369   * Equivalent code is available from RSA Data Security, Inc.
 30370   * This code has been tested against that, and is equivalent, 
 30371   * except that you don't need to include two pages of legalese
 30372   * with every copy.
 30373   *
 30374   * To compute the message digest of a chunk of bytes, declare an
 30375   * MD5Context structure, pass it to MD5Init, call MD5Update as
 30376   * needed on buffers full of bytes, and then call MD5Final, which
 30377   * will fill a supplied 16-byte array with the digest.
 30378   */
 30379  #define SX_MD5_BINSZ	16
 30380  #define SX_MD5_HEXSZ	32
 30381  /*
 30382   * Note: this code is harmless on little-endian machines.
 30383   */
 30384  static void byteReverse (unsigned char *buf, unsigned longs)
 30385  {
 30386  	sxu32 t;
 30387          do {
 30388                  t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
 30389                              ((unsigned)buf[1]<<8 | buf[0]);
 30390                  *(sxu32*)buf = t;
 30391                  buf += 4;
 30392          } while (--longs);
 30393  }
 30394  /* The four core functions - F1 is optimized somewhat */
 30395  
 30396  /* #define F1(x, y, z) (x & y | ~x & z) */
 30397  #ifdef F1
 30398  #undef F1
 30399  #endif
 30400  #ifdef F2
 30401  #undef F2
 30402  #endif
 30403  #ifdef F3
 30404  #undef F3
 30405  #endif
 30406  #ifdef F4
 30407  #undef F4
 30408  #endif
 30409  
 30410  #define F1(x, y, z) (z ^ (x & (y ^ z)))
 30411  #define F2(x, y, z) F1(z, x, y)
 30412  #define F3(x, y, z) (x ^ y ^ z)
 30413  #define F4(x, y, z) (y ^ (x | ~z))
 30414  
 30415  /* This is the central step in the MD5 algorithm.*/
 30416  #define SX_MD5STEP(f, w, x, y, z, data, s) \
 30417          ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
 30418  
 30419  /*
 30420   * The core of the MD5 algorithm, this alters an existing MD5 hash to
 30421   * reflect the addition of 16 longwords of new data.MD5Update blocks
 30422   * the data and converts bytes into longwords for this routine.
 30423   */
 30424  static void MD5Transform(sxu32 buf[4], const sxu32 in[16])
 30425  {
 30426  	register sxu32 a, b, c, d;
 30427  
 30428          a = buf[0];
 30429          b = buf[1];
 30430          c = buf[2];
 30431          d = buf[3];
 30432  
 30433          SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
 30434          SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
 30435          SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
 30436          SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
 30437          SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
 30438          SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
 30439          SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
 30440          SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
 30441          SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
 30442          SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
 30443          SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
 30444          SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
 30445          SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
 30446          SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
 30447          SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
 30448          SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
 30449  
 30450          SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
 30451          SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
 30452          SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
 30453          SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
 30454          SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
 30455          SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
 30456          SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
 30457          SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
 30458          SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
 30459          SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
 30460          SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
 30461          SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
 30462          SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
 30463          SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
 30464          SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
 30465          SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
 30466  
 30467          SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
 30468          SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
 30469          SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
 30470          SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
 30471          SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
 30472          SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
 30473          SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
 30474          SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
 30475          SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
 30476          SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
 30477          SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
 30478          SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
 30479          SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
 30480          SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
 30481          SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
 30482          SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
 30483  
 30484          SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
 30485          SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
 30486          SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
 30487          SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
 30488          SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
 30489          SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
 30490          SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
 30491          SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
 30492          SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
 30493          SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
 30494          SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
 30495          SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
 30496          SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
 30497          SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
 30498          SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
 30499          SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
 30500  
 30501          buf[0] += a;
 30502          buf[1] += b;
 30503          buf[2] += c;
 30504          buf[3] += d;
 30505  }
 30506  /*
 30507   * Update context to reflect the concatenation of another buffer full
 30508   * of bytes.
 30509   */
 30510  JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len)
 30511  {
 30512  	sxu32 t;
 30513  
 30514          /* Update bitcount */
 30515          t = ctx->bits[0];
 30516          if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t)
 30517                  ctx->bits[1]++; /* Carry from low to high */
 30518          ctx->bits[1] += len >> 29;
 30519          t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
 30520          /* Handle any leading odd-sized chunks */
 30521          if ( t ) {
 30522                  unsigned char *p = (unsigned char *)ctx->in + t;
 30523  
 30524                  t = 64-t;
 30525                  if (len < t) {
 30526                          SyMemcpy(buf, p, len);
 30527                          return;
 30528                  }
 30529                  SyMemcpy(buf, p, t);
 30530                  byteReverse(ctx->in, 16);
 30531                  MD5Transform(ctx->buf, (sxu32*)ctx->in);
 30532                  buf += t;
 30533                  len -= t;
 30534          }
 30535          /* Process data in 64-byte chunks */
 30536          while (len >= 64) {
 30537                  SyMemcpy(buf, ctx->in, 64);
 30538                  byteReverse(ctx->in, 16);
 30539                  MD5Transform(ctx->buf, (sxu32*)ctx->in);
 30540                  buf += 64;
 30541                  len -= 64;
 30542          }
 30543          /* Handle any remaining bytes of data.*/
 30544          SyMemcpy(buf, ctx->in, len);
 30545  }
 30546  /*
 30547   * Final wrapup - pad to 64-byte boundary with the bit pattern 
 30548   * 1 0* (64-bit count of bits processed, MSB-first)
 30549   */
 30550  JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){
 30551          unsigned count;
 30552          unsigned char *p;
 30553  
 30554          /* Compute number of bytes mod 64 */
 30555          count = (ctx->bits[0] >> 3) & 0x3F;
 30556  
 30557          /* Set the first char of padding to 0x80.This is safe since there is
 30558             always at least one byte free */
 30559          p = ctx->in + count;
 30560          *p++ = 0x80;
 30561  
 30562          /* Bytes of padding needed to make 64 bytes */
 30563          count = 64 - 1 - count;
 30564  
 30565          /* Pad out to 56 mod 64 */
 30566          if (count < 8) {
 30567                  /* Two lots of padding:  Pad the first block to 64 bytes */
 30568                 SyZero(p, count);
 30569                  byteReverse(ctx->in, 16);
 30570                  MD5Transform(ctx->buf, (sxu32*)ctx->in);
 30571  
 30572                  /* Now fill the next block with 56 bytes */
 30573                  SyZero(ctx->in, 56);
 30574          } else {
 30575                  /* Pad block to 56 bytes */
 30576                  SyZero(p, count-8);
 30577          }
 30578          byteReverse(ctx->in, 14);
 30579  
 30580          /* Append length in bits and transform */
 30581          ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0];
 30582          ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1];
 30583  
 30584          MD5Transform(ctx->buf, (sxu32*)ctx->in);
 30585          byteReverse((unsigned char *)ctx->buf, 4);
 30586          SyMemcpy(ctx->buf, digest, 0x10);
 30587          SyZero(ctx, sizeof(ctx));    /* In case it's sensitive */
 30588  }
 30589  #undef F1
 30590  #undef F2
 30591  #undef F3
 30592  #undef F4
 30593  JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx)
 30594  {	
 30595  	pCtx->buf[0] = 0x67452301;
 30596      pCtx->buf[1] = 0xefcdab89;
 30597      pCtx->buf[2] = 0x98badcfe;
 30598      pCtx->buf[3] = 0x10325476;
 30599      pCtx->bits[0] = 0;
 30600      pCtx->bits[1] = 0;
 30601     
 30602     return SXRET_OK;
 30603  }
 30604  JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16])
 30605  {
 30606  	MD5Context sCtx;
 30607  	MD5Init(&sCtx);
 30608  	MD5Update(&sCtx, (const unsigned char *)pIn, nLen);
 30609  	MD5Final(zDigest, &sCtx);	
 30610  	return SXRET_OK;
 30611  }
 30612  /*
 30613   * SHA-1 in C
 30614   * By Steve Reid <steve@edmweb.com>
 30615   * Status: Public Domain
 30616   */
 30617  /*
 30618   * blk0() and blk() perform the initial expand.
 30619   * I got the idea of expanding during the round function from SSLeay
 30620   *
 30621   * blk0le() for little-endian and blk0be() for big-endian.
 30622   */
 30623  #if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
 30624  /*
 30625   * GCC by itself only generates left rotates.  Use right rotates if
 30626   * possible to be kinder to dinky implementations with iterative rotate
 30627   * instructions.
 30628   */
 30629  #define SHA_ROT(op, x, k) \
 30630          ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; })
 30631  #define rol(x, k) SHA_ROT("roll", x, k)
 30632  #define ror(x, k) SHA_ROT("rorl", x, k)
 30633  
 30634  #else
 30635  /* Generic C equivalent */
 30636  #define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r))
 30637  #define rol(x, k) SHA_ROT(x, k, 32-(k))
 30638  #define ror(x, k) SHA_ROT(x, 32-(k), k)
 30639  #endif
 30640  
 30641  #define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \
 30642      |(rol(block[i], 8)&0x00FF00FF))
 30643  #define blk0be(i) block[i]
 30644  #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
 30645      ^block[(i+2)&15]^block[i&15], 1))
 30646  
 30647  /*
 30648   * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
 30649   *
 30650   * Rl0() for little-endian and Rb0() for big-endian.  Endianness is 
 30651   * determined at run-time.
 30652   */
 30653  #define Rl0(v, w, x, y, z, i) \
 30654      z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
 30655  #define Rb0(v, w, x, y, z, i) \
 30656      z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
 30657  #define R1(v, w, x, y, z, i) \
 30658      z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
 30659  #define R2(v, w, x, y, z, i) \
 30660      z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2);
 30661  #define R3(v, w, x, y, z, i) \
 30662      z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2);
 30663  #define R4(v, w, x, y, z, i) \
 30664      z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2);
 30665  
 30666  /*
 30667   * Hash a single 512-bit block. This is the core of the algorithm.
 30668   */
 30669  #define a qq[0]
 30670  #define b qq[1]
 30671  #define c qq[2]
 30672  #define d qq[3]
 30673  #define e qq[4]
 30674  
 30675  static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
 30676  {
 30677    unsigned int qq[5]; /* a, b, c, d, e; */
 30678    static int one = 1;
 30679    unsigned int block[16];
 30680    SyMemcpy(buffer, (void *)block, 64);
 30681    SyMemcpy(state, qq, 5*sizeof(unsigned int));
 30682  
 30683    /* Copy context->state[] to working vars */
 30684    /*
 30685    a = state[0];
 30686    b = state[1];
 30687    c = state[2];
 30688    d = state[3];
 30689    e = state[4];
 30690    */
 30691  
 30692    /* 4 rounds of 20 operations each. Loop unrolled. */
 30693    if( 1 == *(unsigned char*)&one ){
 30694      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);
 30695      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);
 30696      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);
 30697      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);
 30698    }else{
 30699      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);
 30700      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);
 30701      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);
 30702      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);
 30703    }
 30704    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);
 30705    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);
 30706    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);
 30707    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);
 30708    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);
 30709    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);
 30710    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);
 30711    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);
 30712    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);
 30713    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);
 30714    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);
 30715    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);
 30716    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);
 30717    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);
 30718    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);
 30719    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);
 30720  
 30721    /* Add the working vars back into context.state[] */
 30722    state[0] += a;
 30723    state[1] += b;
 30724    state[2] += c;
 30725    state[3] += d;
 30726    state[4] += e;
 30727  }
 30728  #undef a
 30729  #undef b
 30730  #undef c
 30731  #undef d
 30732  #undef e
 30733  /*
 30734   * SHA1Init - Initialize new context
 30735   */
 30736  JX9_PRIVATE void SHA1Init(SHA1Context *context){
 30737      /* SHA1 initialization constants */
 30738      context->state[0] = 0x67452301;
 30739      context->state[1] = 0xEFCDAB89;
 30740      context->state[2] = 0x98BADCFE;
 30741      context->state[3] = 0x10325476;
 30742      context->state[4] = 0xC3D2E1F0;
 30743      context->count[0] = context->count[1] = 0;
 30744  }
 30745  /*
 30746   * Run your data through this.
 30747   */
 30748  JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){
 30749      unsigned int i, j;
 30750  
 30751      j = context->count[0];
 30752      if ((context->count[0] += len << 3) < j)
 30753  	context->count[1] += (len>>29)+1;
 30754      j = (j >> 3) & 63;
 30755      if ((j + len) > 63) {
 30756  		(void)SyMemcpy(data, &context->buffer[j],  (i = 64-j));
 30757  	SHA1Transform(context->state, context->buffer);
 30758  	for ( ; i + 63 < len; i += 64)
 30759  	    SHA1Transform(context->state, &data[i]);
 30760  	j = 0;
 30761      } else {
 30762  	i = 0;
 30763      }
 30764  	(void)SyMemcpy(&data[i], &context->buffer[j], len - i);
 30765  }
 30766  /*
 30767   * Add padding and return the message digest.
 30768   */
 30769  JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){
 30770      unsigned int i;
 30771      unsigned char finalcount[8];
 30772  
 30773      for (i = 0; i < 8; i++) {
 30774  	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
 30775  	 >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
 30776      }
 30777      SHA1Update(context, (const unsigned char *)"\200", 1);
 30778      while ((context->count[0] & 504) != 448)
 30779  	SHA1Update(context, (const unsigned char *)"\0", 1);
 30780      SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
 30781  
 30782      if (digest) {
 30783  	for (i = 0; i < 20; i++)
 30784  	    digest[i] = (unsigned char)
 30785  		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
 30786      }
 30787  }
 30788  #undef Rl0
 30789  #undef Rb0
 30790  #undef R1
 30791  #undef R2
 30792  #undef R3
 30793  #undef R4
 30794  
 30795  JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20])
 30796  {
 30797  	SHA1Context sCtx;
 30798  	SHA1Init(&sCtx);
 30799  	SHA1Update(&sCtx, (const unsigned char *)pIn, nLen);
 30800  	SHA1Final(&sCtx, zDigest);
 30801  	return SXRET_OK;
 30802  }
 30803  static const sxu32 crc32_table[] = {
 30804  	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 
 30805  	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
 30806  	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 
 30807  	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
 30808  	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 
 30809  	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
 30810  	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 
 30811  	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
 30812  	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 
 30813  	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
 30814  	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 
 30815  	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
 30816  	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 
 30817  	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
 30818  	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 
 30819  	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
 30820  	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 
 30821  	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
 30822  	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 
 30823  	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
 30824  	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 
 30825  	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
 30826  	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 
 30827  	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
 30828  	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 
 30829  	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
 30830  	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 
 30831  	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
 30832  	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 
 30833  	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
 30834  	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 
 30835  	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
 30836  	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 
 30837  	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
 30838  	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 
 30839  	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
 30840  	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 
 30841  	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
 30842  	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 
 30843  	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
 30844  	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 
 30845  	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
 30846  	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 
 30847  	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
 30848  	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 
 30849  	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
 30850  	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 
 30851  	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
 30852  	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 
 30853  	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
 30854  	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 
 30855  	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
 30856  	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 
 30857  	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
 30858  	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 
 30859  	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
 30860  	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 
 30861  	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
 30862  	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 
 30863  	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
 30864  	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 
 30865  	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
 30866  	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 
 30867  	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, 
 30868  };
 30869  #define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) )
 30870  static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen)
 30871  {
 30872  	register unsigned char *zIn = (unsigned char *)pSrc;
 30873  	unsigned char *zEnd;
 30874  	if( zIn == 0 ){
 30875  		return crc32;
 30876  	}
 30877  	zEnd = &zIn[nLen];
 30878  	for(;;){
 30879  		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
 30880  		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
 30881  		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
 30882  		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
 30883  	}
 30884  		
 30885  	return crc32;
 30886  }
 30887  JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen)
 30888  {
 30889  	return SyCrc32Update(SXU32_HIGH, pSrc, nLen);
 30890  }
 30891  #endif /* JX9_DISABLE_HASH_FUNC */
 30892  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 30893  #ifndef JX9_DISABLE_BUILTIN_FUNC
 30894  JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData)
 30895  {
 30896  	static const unsigned char zHexTab[] = "0123456789abcdef";
 30897  	const unsigned char *zIn, *zEnd;
 30898  	unsigned char zOut[3];
 30899  	sxi32 rc;
 30900  #if defined(UNTRUST)
 30901  	if( pIn == 0 || xConsumer == 0 ){
 30902  		return SXERR_EMPTY;
 30903  	}
 30904  #endif
 30905  	zIn   = (const unsigned char *)pIn;
 30906  	zEnd  = &zIn[nLen];
 30907  	for(;;){
 30908  		if( zIn >= zEnd  ){
 30909  			break;
 30910  		}
 30911  		zOut[0] = zHexTab[zIn[0] >> 4];  zOut[1] = zHexTab[zIn[0] & 0x0F];
 30912  		rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData);
 30913  		if( rc != SXRET_OK ){
 30914  			return rc;
 30915  		}
 30916  		zIn++; 
 30917  	}
 30918  	return SXRET_OK;
 30919  }
 30920  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 30921  JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb)
 30922  {
 30923  	buf[3] = nb & 0xFF ; nb >>=8;
 30924  	buf[2] = nb & 0xFF ; nb >>=8;
 30925  	buf[1] = nb & 0xFF ; nb >>=8;
 30926  	buf[0] = (unsigned char)nb ;
 30927  }
 30928  JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB)
 30929  {
 30930  	*uNB = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
 30931  }
 30932  JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb)
 30933  {
 30934  	buf[1] = nb & 0xFF ; nb >>=8;
 30935  	buf[0] = (unsigned char)nb ;
 30936  }
 30937  JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB)
 30938  {
 30939  	*uNB = buf[1] + (buf[0] << 8);
 30940  }
 30941  JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64)
 30942  {
 30943  	buf[7] = n64 & 0xFF; n64 >>=8;
 30944  	buf[6] = n64 & 0xFF; n64 >>=8;
 30945  	buf[5] = n64 & 0xFF; n64 >>=8;
 30946  	buf[4] = n64 & 0xFF; n64 >>=8;
 30947  	buf[3] = n64 & 0xFF; n64 >>=8;
 30948  	buf[2] = n64 & 0xFF; n64 >>=8;
 30949  	buf[1] = n64 & 0xFF; n64 >>=8;
 30950  	buf[0] = (sxu8)n64 ; 
 30951  }
 30952  JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64)
 30953  {
 30954  	sxu32 u1,u2;
 30955  	u1 = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
 30956  	u2 = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
 30957  	*n64 = (((sxu64)u2) << 32) | u1;
 30958  }
 30959  JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64)
 30960  {
 30961  	unsigned char zBuf[8];
 30962  	sxi32 rc;
 30963  	SyBigEndianPack64(zBuf,n64);
 30964  	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
 30965  	return rc;
 30966  }
 30967  JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32)
 30968  {
 30969  	unsigned char zBuf[4];
 30970  	sxi32 rc;
 30971  	SyBigEndianPack32(zBuf,n32);
 30972  	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
 30973  	return rc;
 30974  }
 30975  JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16)
 30976  {
 30977  	unsigned char zBuf[2];
 30978  	sxi32 rc;
 30979  	SyBigEndianPack16(zBuf,n16);
 30980  	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
 30981  	return rc;
 30982  }
 30983  JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut)
 30984  {
 30985  	sxi32 nDate,nTime;
 30986  	nDate = ((pFmt->tm_year - 1980) << 9) + (pFmt->tm_mon << 5) + pFmt->tm_mday;
 30987  	nTime = (pFmt->tm_hour << 11) + (pFmt->tm_min << 5)+ (pFmt->tm_sec >> 1);
 30988  	*pOut = (nDate << 16) | nTime;
 30989  }
 30990  JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut)
 30991  {
 30992  	sxu16 nDate;
 30993  	sxu16 nTime;
 30994  	nDate = nDosDate >> 16;
 30995  	nTime = nDosDate & 0xFFFF;
 30996  	pOut->tm_isdst  = 0;
 30997  	pOut->tm_year 	= 1980 + (nDate >> 9);
 30998  	pOut->tm_mon	= (nDate % (1<<9))>>5;
 30999  	pOut->tm_mday	= (nDate % (1<<9))&0x1F;
 31000  	pOut->tm_hour	= nTime >> 11;
 31001  	pOut->tm_min	= (nTime % (1<<11)) >> 5;
 31002  	pOut->tm_sec	= ((nTime % (1<<11))& 0x1F )<<1;
 31003  }
 31004  /*
 31005   * ----------------------------------------------------------
 31006   * File: jx9_memobj.c
 31007   * MD5: 8692d7f4cb297c0946066b4a9034c637
 31008   * ----------------------------------------------------------
 31009   */
 31010  /*
 31011   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 31012   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 31013   * Version 1.7.2
 31014   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 31015   * please contact Symisc Systems via:
 31016   *       legal@symisc.net
 31017   *       licensing@symisc.net
 31018   *       contact@symisc.net
 31019   * or visit:
 31020   *      http://jx9.symisc.net/
 31021   */
 31022   /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
 31023  #ifndef JX9_AMALGAMATION
 31024  #include "jx9Int.h"
 31025  #endif
 31026  /* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */
 31027  /*
 31028   * Notes on memory objects [i.e: jx9_value].
 31029   * Internally, the JX9 virtual machine manipulates nearly all JX9 values
 31030   * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures.
 31031   * Each jx9_values struct may cache multiple representations (string, 
 31032   * integer etc.) of the same value.
 31033   */
 31034  /*
 31035   * Convert a 64-bit IEEE double into a 64-bit signed integer.
 31036   * If the double is too large, return 0x8000000000000000.
 31037   *
 31038   * Most systems appear to do this simply by assigning ariables and without
 31039   * the extra range tests.
 31040   * But there are reports that windows throws an expection if the floating 
 31041   * point value is out of range.
 31042   */
 31043  static sxi64 MemObjRealToInt(jx9_value *pObj)
 31044  {
 31045  #ifdef JX9_OMIT_FLOATING_POINT
 31046  	/* Real and 64bit integer are the same when floating point arithmetic
 31047  	 * is omitted from the build.
 31048  	 */
 31049  	return pObj->x.rVal;
 31050  #else
 31051   /*
 31052    ** Many compilers we encounter do not define constants for the
 31053    ** minimum and maximum 64-bit integers, or they define them
 31054    ** inconsistently.  And many do not understand the "LL" notation.
 31055    ** So we define our own static constants here using nothing
 31056    ** larger than a 32-bit integer constant.
 31057    */
 31058    static const sxi64 maxInt = LARGEST_INT64;
 31059    static const sxi64 minInt = SMALLEST_INT64;
 31060    jx9_real r = pObj->x.rVal;
 31061    if( r<(jx9_real)minInt ){
 31062      return minInt;
 31063    }else if( r>(jx9_real)maxInt ){
 31064      /* minInt is correct here - not maxInt.  It turns out that assigning
 31065      ** a very large positive number to an integer results in a very large
 31066      ** negative integer.  This makes no sense, but it is what x86 hardware
 31067      ** does so for compatibility we will do the same in software. */
 31068      return minInt;
 31069    }else{
 31070      return (sxi64)r;
 31071    }
 31072  #endif
 31073  }
 31074  /*
 31075   * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal] 
 31076   * to a 64-bit integer.
 31077   */
 31078  JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal)
 31079  {
 31080  	sxi64 iVal = 0;
 31081  	if( pVal->nByte <= 0 ){
 31082  		return 0;
 31083  	}
 31084  	if( pVal->zString[0] == '0' ){
 31085  		sxi32 c;
 31086  		if( pVal->nByte == sizeof(char) ){
 31087  			return 0;
 31088  		}
 31089  		c = pVal->zString[1];
 31090  		if( c  == 'x' || c == 'X' ){
 31091  			/* Hex digit stream */
 31092  			SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
 31093  		}else if( c == 'b' || c == 'B' ){
 31094  			/* Binary digit stream */
 31095  			SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
 31096  		}else{
 31097  			/* Octal digit stream */
 31098  			SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
 31099  		}
 31100  	}else{
 31101  		/* Decimal digit stream */
 31102  		SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
 31103  	}
 31104  	return iVal;
 31105  }
 31106  /*
 31107   * Return some kind of 64-bit integer value which is the best we can
 31108   * do at representing the value that pObj describes as a string
 31109   * representation.
 31110   */
 31111  static sxi64 MemObjStringToInt(jx9_value *pObj)
 31112  {
 31113  	SyString sVal;
 31114  	SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 31115  	return jx9TokenValueToInt64(&sVal);	
 31116  }
 31117  /*
 31118   * Return some kind of integer value which is the best we can
 31119   * do at representing the value that pObj describes as an integer.
 31120   * If pObj is an integer, then the value is exact. If pObj is
 31121   * a floating-point then  the value returned is the integer part.
 31122   * If pObj is a string, then we make an attempt to convert it into
 31123   * a integer and return that. 
 31124   * If pObj represents a NULL value, return 0.
 31125   */
 31126  static sxi64 MemObjIntValue(jx9_value *pObj)
 31127  {
 31128  	sxi32 iFlags;
 31129  	iFlags = pObj->iFlags;
 31130  	if (iFlags & MEMOBJ_REAL ){
 31131  		return MemObjRealToInt(&(*pObj));
 31132  	}else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 31133  		return pObj->x.iVal;
 31134  	}else if (iFlags & MEMOBJ_STRING) {
 31135  		return MemObjStringToInt(&(*pObj));
 31136  	}else if( iFlags & MEMOBJ_NULL ){
 31137  		return 0;
 31138  	}else if( iFlags & MEMOBJ_HASHMAP ){
 31139  		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
 31140  		sxu32 n = pMap->nEntry;
 31141  		jx9HashmapUnref(pMap);
 31142  		/* Return total number of entries in the hashmap */
 31143  		return n; 
 31144  	}else if(iFlags & MEMOBJ_RES ){
 31145  		return pObj->x.pOther != 0;
 31146  	}
 31147  	/* CANT HAPPEN */
 31148  	return 0;
 31149  }
 31150  /*
 31151   * Return some kind of real value which is the best we can
 31152   * do at representing the value that pObj describes as a real.
 31153   * If pObj is a real, then the value is exact.If pObj is an
 31154   * integer then the integer  is promoted to real and that value
 31155   * is returned.
 31156   * If pObj is a string, then we make an attempt to convert it
 31157   * into a real and return that. 
 31158   * If pObj represents a NULL value, return 0.0
 31159   */
 31160  static jx9_real MemObjRealValue(jx9_value *pObj)
 31161  {
 31162  	sxi32 iFlags;
 31163  	iFlags = pObj->iFlags;
 31164  	if( iFlags & MEMOBJ_REAL ){
 31165  		return pObj->x.rVal;
 31166  	}else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
 31167  		return (jx9_real)pObj->x.iVal;
 31168  	}else if (iFlags & MEMOBJ_STRING){
 31169  		SyString sString;
 31170  #ifdef JX9_OMIT_FLOATING_POINT
 31171  		jx9_real rVal = 0;
 31172  #else
 31173  		jx9_real rVal = 0.0;
 31174  #endif
 31175  		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 31176  		if( SyBlobLength(&pObj->sBlob) > 0 ){
 31177  			/* Convert as much as we can */
 31178  #ifdef JX9_OMIT_FLOATING_POINT
 31179  			rVal = MemObjStringToInt(&(*pObj));
 31180  #else
 31181  			SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
 31182  #endif
 31183  		}
 31184  		return rVal;
 31185  	}else if( iFlags & MEMOBJ_NULL ){
 31186  #ifdef JX9_OMIT_FLOATING_POINT
 31187  		return 0;
 31188  #else
 31189  		return 0.0;
 31190  #endif
 31191  	}else if( iFlags & MEMOBJ_HASHMAP ){
 31192  		/* Return the total number of entries in the hashmap */
 31193  		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
 31194  		jx9_real n = (jx9_real)pMap->nEntry;
 31195  		jx9HashmapUnref(pMap);
 31196  		return n;
 31197  	}else if(iFlags & MEMOBJ_RES ){
 31198  		return (jx9_real)(pObj->x.pOther != 0);
 31199  	}
 31200  	/* NOT REACHED  */
 31201  	return 0;
 31202  }
 31203  /* 
 31204   * Return the string representation of a given jx9_value.
 31205   * This function never fail and always return SXRET_OK.
 31206   */
 31207  static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj)
 31208  {
 31209  	if( pObj->iFlags & MEMOBJ_REAL ){
 31210  		SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
 31211  	}else if( pObj->iFlags & MEMOBJ_INT ){
 31212  		SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal);
 31213  		/* %qd (BSD quad) is equivalent to %lld in the libc printf */
 31214  	}else if( pObj->iFlags & MEMOBJ_BOOL ){
 31215  		if( pObj->x.iVal ){
 31216  			SyBlobAppend(&(*pOut),"true", sizeof("true")-1);
 31217  		}else{
 31218  			SyBlobAppend(&(*pOut),"false", sizeof("false")-1);
 31219  		}
 31220  	}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
 31221  		/* Serialize JSON object or array */
 31222  		jx9JsonSerialize(pObj,pOut);
 31223  		jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
 31224  	}else if(pObj->iFlags & MEMOBJ_RES ){
 31225  		SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther);
 31226  	}
 31227  	return SXRET_OK;
 31228  }
 31229  /*
 31230   * Return some kind of boolean value which is the best we can do
 31231   * at representing the value that pObj describes as a boolean.
 31232   * When converting to boolean, the following values are considered FALSE:
 31233   * NULL
 31234   * the boolean FALSE itself.
 31235   * the integer 0 (zero).
 31236   * the real 0.0 (zero).
 31237   * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string
 31238   * "false".
 31239   * an array with zero elements. 
 31240   */
 31241  static sxi32 MemObjBooleanValue(jx9_value *pObj)
 31242  {
 31243  	sxi32 iFlags;	
 31244  	iFlags = pObj->iFlags;
 31245  	if (iFlags & MEMOBJ_REAL ){
 31246  #ifdef JX9_OMIT_FLOATING_POINT
 31247  		return pObj->x.rVal ? 1 : 0;
 31248  #else
 31249  		return pObj->x.rVal != 0.0 ? 1 : 0;
 31250  #endif
 31251  	}else if( iFlags & MEMOBJ_INT ){
 31252  		return pObj->x.iVal ? 1 : 0;
 31253  	}else if (iFlags & MEMOBJ_STRING) {
 31254  		SyString sString;
 31255  		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 31256  		if( sString.nByte == 0 ){
 31257  			/* Empty string */
 31258  			return 0;
 31259  		}else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) ||
 31260  			(sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) ||
 31261  			(sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){
 31262  				return 1;
 31263  		}else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){
 31264  			return 0;
 31265  		}else{
 31266  			const char *zIn, *zEnd;
 31267  			zIn = sString.zString;
 31268  			zEnd = &zIn[sString.nByte];
 31269  			while( zIn < zEnd && zIn[0] == '0' ){
 31270  				zIn++;
 31271  			}
 31272  			return zIn >= zEnd ? 0 : 1;
 31273  		}
 31274  	}else if( iFlags & MEMOBJ_NULL ){
 31275  		return 0;
 31276  	}else if( iFlags & MEMOBJ_HASHMAP ){
 31277  		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
 31278  		sxu32 n = pMap->nEntry;
 31279  		jx9HashmapUnref(pMap);
 31280  		return n > 0 ? TRUE : FALSE;
 31281  	}else if(iFlags & MEMOBJ_RES ){
 31282  		return pObj->x.pOther != 0;
 31283  	}
 31284  	/* NOT REACHED */
 31285  	return 0;
 31286  }
 31287  /*
 31288   * If the jx9_value is of type real, try to make it an integer also.
 31289   */
 31290  static sxi32 MemObjTryIntger(jx9_value *pObj)
 31291  {
 31292  	sxi64 iVal = MemObjRealToInt(&(*pObj));
 31293    /* Only mark the value as an integer if
 31294    **
 31295    **    (1) the round-trip conversion real->int->real is a no-op, and
 31296    **    (2) The integer is neither the largest nor the smallest
 31297    **        possible integer
 31298    **
 31299    ** The second and third terms in the following conditional enforces
 31300    ** the second condition under the assumption that addition overflow causes
 31301    ** values to wrap around.  On x86 hardware, the third term is always
 31302    ** true and could be omitted.  But we leave it in because other
 31303    ** architectures might behave differently.
 31304    */
 31305  	if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){
 31306  		pObj->x.iVal = iVal; 
 31307  		pObj->iFlags = MEMOBJ_INT;
 31308  	}
 31309  	return SXRET_OK;
 31310  }
 31311  /*
 31312   * Convert a jx9_value to type integer.Invalidate any prior representations.
 31313   */
 31314  JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj)
 31315  {
 31316  	if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
 31317  		/* Preform the conversion */
 31318  		pObj->x.iVal = MemObjIntValue(&(*pObj));
 31319  		/* Invalidate any prior representations */
 31320  		SyBlobRelease(&pObj->sBlob);
 31321  		MemObjSetType(pObj, MEMOBJ_INT);
 31322  	}
 31323  	return SXRET_OK;
 31324  }
 31325  /*
 31326   * Convert a jx9_value to type real (Try to get an integer representation also).
 31327   * Invalidate any prior representations
 31328   */
 31329  JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj)
 31330  {
 31331  	if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
 31332  		/* Preform the conversion */
 31333  		pObj->x.rVal = MemObjRealValue(&(*pObj));
 31334  		/* Invalidate any prior representations */
 31335  		SyBlobRelease(&pObj->sBlob);
 31336  		MemObjSetType(pObj, MEMOBJ_REAL);
 31337  	}
 31338  	return SXRET_OK;
 31339  }
 31340  /*
 31341   * Convert a jx9_value to type boolean.Invalidate any prior representations.
 31342   */
 31343  JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj)
 31344  {
 31345  	if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
 31346  		/* Preform the conversion */
 31347  		pObj->x.iVal = MemObjBooleanValue(&(*pObj));
 31348  		/* Invalidate any prior representations */
 31349  		SyBlobRelease(&pObj->sBlob);
 31350  		MemObjSetType(pObj, MEMOBJ_BOOL);
 31351  	}
 31352  	return SXRET_OK;
 31353  }
 31354  /*
 31355   * Convert a jx9_value to type string.Prior representations are NOT invalidated.
 31356   */
 31357  JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj)
 31358  {
 31359  	sxi32 rc = SXRET_OK;
 31360  	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
 31361  		/* Perform the conversion */
 31362  		SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
 31363  		rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
 31364  		MemObjSetType(pObj, MEMOBJ_STRING);
 31365  	}
 31366  	return rc;
 31367  }
 31368  /*
 31369   * Nullify a jx9_value.In other words invalidate any prior
 31370   * representation.
 31371   */
 31372  JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj)
 31373  {
 31374  	return jx9MemObjRelease(pObj);
 31375  }
 31376  /*
 31377   * Convert a jx9_value to type array.Invalidate any prior representations.
 31378    * According to the JX9 language reference manual.
 31379    *   For any of the types: integer, float, string, boolean converting a value
 31380    *   to an array results in an array with a single element with index zero 
 31381    *   and the value of the scalar which was converted.
 31382    */
 31383  JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj)
 31384  {
 31385  	if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
 31386  		jx9_hashmap *pMap;
 31387  		/* Allocate a new hashmap instance */
 31388  		pMap = jx9NewHashmap(pObj->pVm, 0, 0);
 31389  		if( pMap == 0 ){
 31390  			return SXERR_MEM;
 31391  		}
 31392  		if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
 31393  			/* 
 31394  			 * According to the JX9 language reference manual.
 31395  			 *   For any of the types: integer, float, string, boolean converting a value
 31396  			 *   to an array results in an array with a single element with index zero 
 31397  			 *   and the value of the scalar which was converted.
 31398  			 */
 31399  			/* Insert a single element */
 31400  			jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj));
 31401  			SyBlobRelease(&pObj->sBlob);
 31402  		}
 31403  		/* Invalidate any prior representation */
 31404  		MemObjSetType(pObj, MEMOBJ_HASHMAP);
 31405  		pObj->x.pOther = pMap;
 31406  	}
 31407  	return SXRET_OK;
 31408  }
 31409  /*
 31410   * Return a pointer to the appropriate convertion method associated 
 31411   * with the given type. 
 31412   * Note on type juggling.
 31413   * Accoding to the JX9 language reference manual
 31414   *  JX9 does not require (or support) explicit type definition in variable
 31415   *  declaration; a variable's type is determined by the context in which
 31416   *  the variable is used. That is to say, if a string value is assigned 
 31417   *  to variable $var, $var becomes a string. If an integer value is then
 31418   *  assigned to $var, it becomes an integer. 
 31419   */
 31420  JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags)
 31421  {
 31422  	if( iFlags & MEMOBJ_STRING ){
 31423  		return jx9MemObjToString;
 31424  	}else if( iFlags & MEMOBJ_INT ){
 31425  		return jx9MemObjToInteger;
 31426  	}else if( iFlags & MEMOBJ_REAL ){
 31427  		return jx9MemObjToReal;
 31428  	}else if( iFlags & MEMOBJ_BOOL ){
 31429  		return jx9MemObjToBool;
 31430  	}else if( iFlags & MEMOBJ_HASHMAP ){
 31431  		return jx9MemObjToHashmap;
 31432  	}
 31433  	/* NULL cast */
 31434  	return jx9MemObjToNull;
 31435  }
 31436  /*
 31437   * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks
 31438   * like a numeric number [i.e: if the jx9_value is of type string.].
 31439   * Return TRUE if numeric.FALSE otherwise.
 31440   */
 31441  JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj)
 31442  {
 31443  	if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
 31444  		return TRUE;
 31445  	}else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
 31446  		return FALSE;
 31447  	}else if( pObj->iFlags & MEMOBJ_STRING ){
 31448  		SyString sStr;
 31449  		sxi32 rc;
 31450  		SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 31451  		if( sStr.nByte <= 0 ){
 31452  			/* Empty string */
 31453  			return FALSE;
 31454  		}
 31455  		/* Check if the string representation looks like a numeric number */
 31456  		rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0);
 31457  		return rc == SXRET_OK ? TRUE : FALSE;
 31458  	}
 31459  	/* NOT REACHED */
 31460  	return FALSE;
 31461  }
 31462  /*
 31463   * Check whether the jx9_value is empty.Return TRUE if empty.
 31464   * FALSE otherwise.
 31465   * An jx9_value is considered empty if the following are true:
 31466   * NULL value.
 31467   * Boolean FALSE.
 31468   * Integer/Float with a 0 (zero) value.
 31469   * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...].
 31470   * An empty array.
 31471   * NOTE
 31472   *  OBJECT VALUE MUST NOT BE MODIFIED.
 31473   */
 31474  JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj)
 31475  {
 31476  	if( pObj->iFlags & MEMOBJ_NULL ){
 31477  		return TRUE;
 31478  	}else if( pObj->iFlags & MEMOBJ_INT ){
 31479  		return pObj->x.iVal == 0 ? TRUE : FALSE;
 31480  	}else if( pObj->iFlags & MEMOBJ_REAL ){
 31481  		return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE;
 31482  	}else if( pObj->iFlags & MEMOBJ_BOOL ){
 31483  		return !pObj->x.iVal;
 31484  	}else if( pObj->iFlags & MEMOBJ_STRING ){
 31485  		if( SyBlobLength(&pObj->sBlob) <= 0 ){
 31486  			return TRUE;
 31487  		}else{
 31488  			const char *zIn, *zEnd;
 31489  			zIn = (const char *)SyBlobData(&pObj->sBlob);
 31490  			zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
 31491  			while( zIn < zEnd ){
 31492  				if( zIn[0] != '0' ){
 31493  					break;
 31494  				}
 31495  				zIn++;
 31496  			}
 31497  			return zIn >= zEnd ? TRUE : FALSE;
 31498  		}
 31499  	}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
 31500  		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
 31501  		return pMap->nEntry == 0 ? TRUE : FALSE;
 31502  	}else if ( pObj->iFlags & (MEMOBJ_RES) ){
 31503  		return FALSE;
 31504  	}
 31505  	/* Assume empty by default */
 31506  	return TRUE;
 31507  }
 31508  /*
 31509   * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
 31510   * or both.
 31511   * Invalidate any prior representations. Every effort is made to force
 31512   * the conversion, even if the input is a string that does not look 
 31513   * completely like a number.Convert as much of the string as we can
 31514   * and ignore the rest.
 31515   */
 31516  JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj)
 31517  {
 31518  	if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
 31519  		if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
 31520  			if( pObj->iFlags & MEMOBJ_NULL ){
 31521  				pObj->x.iVal = 0;
 31522  			}
 31523  			MemObjSetType(pObj, MEMOBJ_INT);
 31524  		}
 31525  		/* Already numeric */
 31526  		return  SXRET_OK;
 31527  	}
 31528  	if( pObj->iFlags & MEMOBJ_STRING ){
 31529  		sxi32 rc = SXERR_INVALID;
 31530  		sxu8 bReal = FALSE;
 31531  		SyString sString;
 31532  		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 31533  		/* Check if the given string looks like a numeric number */
 31534  		if( sString.nByte > 0 ){
 31535  			rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0);
 31536  		}
 31537  		if( bReal ){
 31538  			jx9MemObjToReal(&(*pObj));
 31539  		}else{
 31540  			if( rc != SXRET_OK ){
 31541  				/* The input does not look at all like a number, set the value to 0 */
 31542  				pObj->x.iVal = 0;
 31543  			}else{
 31544  				/* Convert as much as we can */
 31545  				pObj->x.iVal = MemObjStringToInt(&(*pObj));
 31546  			}
 31547  			MemObjSetType(pObj, MEMOBJ_INT);
 31548  			SyBlobRelease(&pObj->sBlob);
 31549  		}
 31550  	}else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){
 31551  		jx9MemObjToInteger(pObj);
 31552  	}else{
 31553  		/* Perform a blind cast */
 31554  		jx9MemObjToReal(&(*pObj));
 31555  	}
 31556  	return SXRET_OK;
 31557  }
 31558  /*
 31559   * Try a get an integer representation of the given jx9_value.
 31560   * If the jx9_value is not of type real, this function is a no-op.
 31561   */
 31562  JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj)
 31563  {
 31564  	if( pObj->iFlags & MEMOBJ_REAL ){
 31565  		/* Work only with reals */
 31566  		MemObjTryIntger(&(*pObj));
 31567  	}
 31568  	return SXRET_OK;
 31569  }
 31570  /*
 31571   * Initialize a jx9_value to the null type.
 31572   */
 31573  JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj)
 31574  {
 31575  	/* Zero the structure */
 31576  	SyZero(pObj, sizeof(jx9_value));
 31577  	/* Initialize fields */
 31578  	pObj->pVm = pVm;
 31579  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31580  	/* Set the NULL type */
 31581  	pObj->iFlags = MEMOBJ_NULL;
 31582  	return SXRET_OK;
 31583  }
 31584  /*
 31585   * Initialize a jx9_value to the integer type.
 31586   */
 31587  JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal)
 31588  {
 31589  	/* Zero the structure */
 31590  	SyZero(pObj, sizeof(jx9_value));
 31591  	/* Initialize fields */
 31592  	pObj->pVm = pVm;
 31593  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31594  	/* Set the desired type */
 31595  	pObj->x.iVal = iVal;
 31596  	pObj->iFlags = MEMOBJ_INT;
 31597  	return SXRET_OK;
 31598  }
 31599  /*
 31600   * Initialize a jx9_value to the boolean type.
 31601   */
 31602  JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal)
 31603  {
 31604  	/* Zero the structure */
 31605  	SyZero(pObj, sizeof(jx9_value));
 31606  	/* Initialize fields */
 31607  	pObj->pVm = pVm;
 31608  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31609  	/* Set the desired type */
 31610  	pObj->x.iVal = iVal ? 1 : 0;
 31611  	pObj->iFlags = MEMOBJ_BOOL;
 31612  	return SXRET_OK;
 31613  }
 31614  #if 0
 31615  /*
 31616   * Initialize a jx9_value to the real type.
 31617   */
 31618  JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal)
 31619  {
 31620  	/* Zero the structure */
 31621  	SyZero(pObj, sizeof(jx9_value));
 31622  	/* Initialize fields */
 31623  	pObj->pVm = pVm;
 31624  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31625  	/* Set the desired type */
 31626  	pObj->x.rVal = rVal;
 31627  	pObj->iFlags = MEMOBJ_REAL;
 31628  	return SXRET_OK;
 31629  }
 31630  #endif
 31631  /*
 31632   * Initialize a jx9_value to the array type.
 31633   */
 31634  JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray)
 31635  {
 31636  	/* Zero the structure */
 31637  	SyZero(pObj, sizeof(jx9_value));
 31638  	/* Initialize fields */
 31639  	pObj->pVm = pVm;
 31640  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31641  	/* Set the desired type */
 31642  	pObj->iFlags = MEMOBJ_HASHMAP;
 31643  	pObj->x.pOther = pArray;
 31644  	return SXRET_OK;
 31645  }
 31646  /*
 31647   * Initialize a jx9_value to the string type.
 31648   */
 31649  JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal)
 31650  {
 31651  	/* Zero the structure */
 31652  	SyZero(pObj, sizeof(jx9_value));
 31653  	/* Initialize fields */
 31654  	pObj->pVm = pVm;
 31655  	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
 31656  	if( pVal ){
 31657  		/* Append contents */
 31658  		SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte);
 31659  	}
 31660  	/* Set the desired type */
 31661  	pObj->iFlags = MEMOBJ_STRING;
 31662  	return SXRET_OK;
 31663  }
 31664  /*
 31665   * Append some contents to the internal buffer of a given jx9_value.
 31666   * If the given jx9_value is not of type string, this function
 31667   * invalidate any prior representation and set the string type.
 31668   * Then a simple append operation is performed.
 31669   */
 31670  JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen)
 31671  {
 31672  	sxi32 rc;
 31673  	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
 31674  		/* Invalidate any prior representation */
 31675  		jx9MemObjRelease(pObj);
 31676  		MemObjSetType(pObj, MEMOBJ_STRING);
 31677  	}
 31678  	/* Append contents */
 31679  	rc = SyBlobAppend(&pObj->sBlob, zData, nLen);
 31680  	return rc;
 31681  }
 31682  #if 0
 31683  /*
 31684   * Format and append some contents to the internal buffer of a given jx9_value.
 31685   * If the given jx9_value is not of type string, this function invalidate
 31686   * any prior representation and set the string type.
 31687   * Then a simple format and append operation is performed.
 31688   */
 31689  JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap)
 31690  {
 31691  	sxi32 rc;
 31692  	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
 31693  		/* Invalidate any prior representation */
 31694  		jx9MemObjRelease(pObj);
 31695  		MemObjSetType(pObj, MEMOBJ_STRING);
 31696  	}
 31697  	/* Format and append contents */
 31698  	rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap);
 31699  	return rc;
 31700  }
 31701  #endif
 31702  /*
 31703   * Duplicate the contents of a jx9_value.
 31704   */
 31705  JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest)
 31706  {
 31707  	jx9_hashmap *pMap = 0;
 31708  	sxi32 rc;
 31709  	if( pSrc->iFlags & MEMOBJ_HASHMAP ){
 31710  		/* Increment reference count */
 31711  		((jx9_hashmap *)pSrc->x.pOther)->iRef++;
 31712  	}
 31713  	if( pDest->iFlags & MEMOBJ_HASHMAP ){
 31714  		pMap = (jx9_hashmap *)pDest->x.pOther;
 31715  	}
 31716  	SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
 31717  	rc = SXRET_OK;
 31718  	if( SyBlobLength(&pSrc->sBlob) > 0 ){
 31719  		SyBlobReset(&pDest->sBlob);
 31720  		rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob);
 31721  	}else{
 31722  		if( SyBlobLength(&pDest->sBlob) > 0 ){
 31723  			SyBlobRelease(&pDest->sBlob);
 31724  		}
 31725  	}
 31726  	if( pMap ){
 31727  		jx9HashmapUnref(pMap);
 31728  	}
 31729  	return rc;
 31730  }
 31731  /*
 31732   * Duplicate the contents of a jx9_value but do not copy internal
 31733   * buffer contents, simply point to it.
 31734   */
 31735  JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest)
 31736  {
 31737  	SyMemcpy((const void *)&(*pSrc), &(*pDest), 
 31738  		sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
 31739  	if( pSrc->iFlags & MEMOBJ_HASHMAP ){
 31740  		/* Increment reference count */
 31741  		((jx9_hashmap *)pSrc->x.pOther)->iRef++;
 31742  	}
 31743  	if( SyBlobLength(&pDest->sBlob) > 0 ){
 31744  		SyBlobRelease(&pDest->sBlob);
 31745  	}
 31746  	if( SyBlobLength(&pSrc->sBlob) > 0 ){
 31747  		SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob));
 31748  	}
 31749  	return SXRET_OK;
 31750  }
 31751  /*
 31752   * Invalidate any prior representation of a given jx9_value.
 31753   */
 31754  JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj)
 31755  {
 31756  	if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
 31757  		if( pObj->iFlags & MEMOBJ_HASHMAP ){
 31758  			jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
 31759  		}
 31760  		/* Release the internal buffer */
 31761  		SyBlobRelease(&pObj->sBlob);
 31762  		/* Invalidate any prior representation */
 31763  		pObj->iFlags = MEMOBJ_NULL;
 31764  	}
 31765  	return SXRET_OK;
 31766  }
 31767  /*
 31768   * Compare two jx9_values.
 31769   * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
 31770   * or < 0 if pObj2 is greater than pObj1.
 31771   * Type comparison table taken from the JX9 language reference manual.
 31772   * Comparisons of $x with JX9 functions Expression
 31773   *              gettype() 	empty() 	is_null() 	isset() 	boolean : if($x)
 31774   * $x = ""; 	string 	    TRUE 	FALSE 	TRUE 	FALSE
 31775   * $x = null 	NULL 	    TRUE 	TRUE 	FALSE 	FALSE
 31776   * var $x; 	    NULL 	TRUE 	TRUE 	FALSE 	FALSE
 31777   * $x is undefined 	NULL 	TRUE 	TRUE 	FALSE 	FALSE
 31778   *  $x = array(); 	array 	TRUE 	FALSE 	TRUE 	FALSE
 31779   * $x = false; 	boolean 	TRUE 	FALSE 	TRUE 	FALSE
 31780   * $x = true; 	boolean 	FALSE 	FALSE 	TRUE 	TRUE
 31781   * $x = 1; 	    integer 	FALSE 	FALSE 	TRUE 	TRUE
 31782   * $x = 42; 	integer 	FALSE 	FALSE 	TRUE 	TRUE
 31783   * $x = 0; 	    integer 	TRUE 	FALSE 	TRUE 	FALSE
 31784   * $x = -1; 	integer 	FALSE 	FALSE 	TRUE 	TRUE
 31785   * $x = "1"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
 31786   * $x = "0"; 	string 	TRUE 	FALSE 	TRUE 	FALSE
 31787   * $x = "-1"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
 31788   * $x = "jx9"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
 31789   * $x = "true"; string 	FALSE 	FALSE 	TRUE 	TRUE
 31790   * $x = "false"; string 	FALSE 	FALSE 	TRUE 	TRUE
 31791   *      Loose comparisons with == 
 31792   * TRUE 	FALSE 	1 	0 	-1 	"1" 	"0" 	"-1" 	NULL 	array() 	"jx9" 	""
 31793   * TRUE 	TRUE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE
 31794   * FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE
 31795   * 1 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31796   * 0 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE
 31797   * -1 	TRUE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE
 31798   * "1" 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31799   * "0" 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31800   * "-1" 	TRUE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE
 31801   * NULL 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE
 31802   * array() 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	TRUE 	FALSE 	FALSE
 31803   * "jx9" 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE
 31804   * "" 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE
 31805   *    Strict comparisons with === 
 31806   * TRUE 	FALSE 	1 	0 	-1 	"1" 	"0" 	"-1" 	NULL 	array() 	"jx9" 	""
 31807   * TRUE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31808   * FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31809   * 1 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31810   * 0 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31811   * -1 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31812   * "1" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31813   * "0" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
 31814   * "-1" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 
 31815   * NULL 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE
 31816   * array() 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE
 31817   * "jx9" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE
 31818   * "" 	    FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE
 31819   */
 31820  JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest)
 31821  {
 31822  	sxi32 iComb; 
 31823  	sxi32 rc;
 31824  	if( bStrict ){
 31825  		sxi32 iF1, iF2;
 31826  		/* Strict comparisons with === */
 31827  		iF1 = pObj1->iFlags;
 31828  		iF2 = pObj2->iFlags;
 31829  		if( iF1 != iF2 ){
 31830  			/* Not of the same type */
 31831  			return 1;
 31832  		}
 31833  	}
 31834  	/* Combine flag together */
 31835  	iComb = pObj1->iFlags|pObj2->iFlags;
 31836  	if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){
 31837  		/* Convert to boolean: Keep in mind FALSE < TRUE */
 31838  		if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
 31839  			jx9MemObjToBool(pObj1);
 31840  		}
 31841  		if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
 31842  			jx9MemObjToBool(pObj2);
 31843  		}
 31844  		return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
 31845  	}else if ( iComb & MEMOBJ_HASHMAP ){
 31846  		/* Hashmap aka 'array' comparison */
 31847  		if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
 31848  			/* Array is always greater */
 31849  			return -1;
 31850  		}
 31851  		if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
 31852  			/* Array is always greater */
 31853  			return 1;
 31854  		}
 31855  		/* Perform the comparison */
 31856  		rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict);
 31857  		return rc;
 31858  	}else if ( iComb & MEMOBJ_STRING ){
 31859  		SyString s1, s2;
 31860  		/* Perform a strict string comparison.*/
 31861  		if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
 31862  			jx9MemObjToString(pObj1);
 31863  		}
 31864  		if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
 31865  			jx9MemObjToString(pObj2);
 31866  		}
 31867  		SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob));
 31868  		SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob));
 31869  		/*
 31870  		 * Strings are compared using memcmp(). If one value is an exact prefix of the
 31871  		 * other, then the shorter value is less than the longer value.
 31872  		 */
 31873  		rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte));
 31874  		if( rc == 0 ){
 31875  			if( s1.nByte != s2.nByte ){
 31876  				rc = s1.nByte < s2.nByte ? -1 : 1;
 31877  			}
 31878  		}
 31879  		return rc;
 31880  	}else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
 31881  		/* Perform a numeric comparison if one of the operand is numeric(integer or real) */
 31882  		if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
 31883  			jx9MemObjToNumeric(pObj1);
 31884  		}
 31885  		if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
 31886  			jx9MemObjToNumeric(pObj2);
 31887  		}
 31888  		if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
 31889  			jx9_real r1, r2;
 31890  			/* Compare as reals */
 31891  			if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
 31892  				jx9MemObjToReal(pObj1);
 31893  			}
 31894  			r1 = pObj1->x.rVal;	
 31895  			if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
 31896  				jx9MemObjToReal(pObj2);
 31897  			}
 31898  			r2 = pObj2->x.rVal;
 31899  			if( r1 > r2 ){
 31900  				return 1;
 31901  			}else if( r1 < r2 ){
 31902  				return -1;
 31903  			}
 31904  			return 0;
 31905  		}else{
 31906  			/* Integer comparison */
 31907  			if( pObj1->x.iVal > pObj2->x.iVal ){
 31908  				return 1;
 31909  			}else if( pObj1->x.iVal < pObj2->x.iVal ){
 31910  				return -1;
 31911  			}
 31912  			return 0;
 31913  		}
 31914  	}
 31915  	/* NOT REACHED */
 31916  	SXUNUSED(iNest);
 31917  	return 0;
 31918  }
 31919  /*
 31920   * Perform an addition operation of two jx9_values.
 31921   * The reason this function is implemented here rather than 'vm.c'
 31922   * is that the '+' operator is overloaded.
 31923   * That is, the '+' operator is used for arithmetic operation and also 
 31924   * used for operation on arrays [i.e: union]. When used with an array 
 31925   * The + operator returns the right-hand array appended to the left-hand array.
 31926   * For keys that exist in both arrays, the elements from the left-hand array
 31927   * will be used, and the matching elements from the right-hand array will
 31928   * be ignored.
 31929   * This function take care of handling all the scenarios.
 31930   */
 31931  JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore)
 31932  {
 31933  	if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
 31934  			/* Arithemtic operation */
 31935  			jx9MemObjToNumeric(pObj1);
 31936  			jx9MemObjToNumeric(pObj2);
 31937  			if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
 31938  				/* Floating point arithmetic */
 31939  				jx9_real a, b;
 31940  				if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
 31941  					jx9MemObjToReal(pObj1);
 31942  				}
 31943  				if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
 31944  					jx9MemObjToReal(pObj2);
 31945  				}
 31946  				a = pObj1->x.rVal;
 31947  				b = pObj2->x.rVal;
 31948  				pObj1->x.rVal = a+b;
 31949  				MemObjSetType(pObj1, MEMOBJ_REAL);
 31950  				/* Try to get an integer representation also */
 31951  				MemObjTryIntger(&(*pObj1));
 31952  			}else{
 31953  				/* Integer arithmetic */
 31954  				sxi64 a, b;
 31955  				a = pObj1->x.iVal;
 31956  				b = pObj2->x.iVal;
 31957  				pObj1->x.iVal = a+b;
 31958  				MemObjSetType(pObj1, MEMOBJ_INT);
 31959  			}
 31960  	}else{
 31961  		if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
 31962  			jx9_hashmap *pMap;
 31963  			sxi32 rc;
 31964  			if( bAddStore ){
 31965  				/* Do not duplicate the hashmap, use the left one since its an add&store operation.
 31966  				 */
 31967  				if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){				
 31968  					/* Force a hashmap cast */
 31969  					rc = jx9MemObjToHashmap(pObj1);
 31970  					if( rc != SXRET_OK ){
 31971  						jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
 31972  						return rc;
 31973  					}
 31974  				}
 31975  				/* Point to the structure that describe the hashmap */
 31976  				pMap = (jx9_hashmap *)pObj1->x.pOther;
 31977  			}else{
 31978  				/* Create a new hashmap */
 31979  				pMap = jx9NewHashmap(pObj1->pVm, 0, 0);
 31980  				if( pMap == 0){
 31981  					jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
 31982  					return SXERR_MEM;
 31983  				}
 31984  			}
 31985  			if( !bAddStore ){
 31986  				if(pObj1->iFlags & MEMOBJ_HASHMAP ){
 31987  					/* Perform a hashmap duplication */
 31988  					jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap);
 31989  				}else{
 31990  					if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
 31991  						/* Simple insertion */
 31992  						jx9HashmapInsert(pMap, 0, pObj1);
 31993  					}
 31994  				}
 31995  			}
 31996  			/* Perform the union */
 31997  			if(pObj2->iFlags & MEMOBJ_HASHMAP ){
 31998  				jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther);
 31999  			}else{
 32000  				if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
 32001  					/* Simple insertion */
 32002  					jx9HashmapInsert(pMap, 0, pObj2);
 32003  				}
 32004  			}
 32005  			/* Reflect the change */
 32006  			if( pObj1->iFlags & MEMOBJ_STRING ){
 32007  				SyBlobRelease(&pObj1->sBlob);
 32008  			}
 32009  			pObj1->x.pOther = pMap;
 32010  			MemObjSetType(pObj1, MEMOBJ_HASHMAP);
 32011  		}
 32012  	}
 32013  	return SXRET_OK;
 32014  }
 32015  /*
 32016   * Return a printable representation of the type of a given 
 32017   * jx9_value.
 32018   */
 32019  JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal)
 32020  {
 32021  	const char *zType = "";
 32022  	if( pVal->iFlags & MEMOBJ_NULL ){
 32023  		zType = "null";
 32024  	}else if( pVal->iFlags & MEMOBJ_INT ){
 32025  		zType = "int";
 32026  	}else if( pVal->iFlags & MEMOBJ_REAL ){
 32027  		zType = "float";
 32028  	}else if( pVal->iFlags & MEMOBJ_STRING ){
 32029  		zType = "string";
 32030  	}else if( pVal->iFlags & MEMOBJ_BOOL ){
 32031  		zType = "bool";
 32032  	}else if( pVal->iFlags & MEMOBJ_HASHMAP ){
 32033  		jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther;
 32034  		if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
 32035  			zType = "JSON Object";
 32036  		}else{
 32037  			zType = "JSON Array";
 32038  		}
 32039  	}else if( pVal->iFlags & MEMOBJ_RES ){
 32040  		zType = "resource";
 32041  	}
 32042  	return zType;
 32043  }
 32044  /*
 32045   * Dump a jx9_value [i.e: get a printable representation of it's type and contents.].
 32046   * Store the dump in the given blob.
 32047   */
 32048  JX9_PRIVATE sxi32 jx9MemObjDump(
 32049  	SyBlob *pOut,      /* Store the dump here */
 32050  	jx9_value *pObj   /* Dump this */
 32051  	)
 32052  {
 32053  	sxi32 rc = SXRET_OK;
 32054  	const char *zType;
 32055  	/* Get value type first */
 32056  	zType = jx9MemObjTypeDump(pObj);
 32057  	SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
 32058  	if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
 32059  		SyBlobAppend(&(*pOut), "(", sizeof(char));
 32060  		if( pObj->iFlags & MEMOBJ_HASHMAP ){
 32061  			jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
 32062  			SyBlobFormat(pOut,"%u ",pMap->nEntry);
 32063  			/* Dump hashmap entries */
 32064  			rc = jx9JsonSerialize(pObj,pOut);
 32065  		}else{
 32066  			SyBlob *pContents = &pObj->sBlob;
 32067  			/* Get a printable representation of the contents */
 32068  			if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
 32069  				MemObjStringValue(&(*pOut), &(*pObj));
 32070  			}else{
 32071  				/* Append length first */
 32072  				SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob));
 32073  				if( SyBlobLength(pContents) > 0 ){
 32074  					SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents));
 32075  				}
 32076  				SyBlobAppend(&(*pOut), "'", sizeof(char));
 32077  			}
 32078  		}
 32079  		SyBlobAppend(&(*pOut), ")", sizeof(char));	
 32080  	}
 32081  #ifdef __WINNT__
 32082  	SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1);
 32083  #else
 32084  	SyBlobAppend(&(*pOut), "\n", sizeof(char));
 32085  #endif
 32086  	return rc;
 32087  }
 32088  /*
 32089   * ----------------------------------------------------------
 32090   * File: jx9_parse.c
 32091   * MD5: d8fcac4c6cd7672f0103c0bf4a4b61fc
 32092   * ----------------------------------------------------------
 32093   */
 32094  /*
 32095   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 32096   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 32097   * Version 1.7.2
 32098   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 32099   * please contact Symisc Systems via:
 32100   *       legal@symisc.net
 32101   *       licensing@symisc.net
 32102   *       contact@symisc.net
 32103   * or visit:
 32104   *      http://jx9.symisc.net/
 32105   */
 32106   /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */
 32107  #ifndef JX9_AMALGAMATION
 32108  #include "jx9Int.h"
 32109  #endif
 32110  /* Expression parser for the Jx9 programming language */
 32111  /* Operators associativity */
 32112  #define EXPR_OP_ASSOC_LEFT   0x01 /* Left associative operator */
 32113  #define EXPR_OP_ASSOC_RIGHT  0x02 /* Right associative operator */
 32114  #define EXPR_OP_NON_ASSOC    0x04 /* Non-associative operator */
 32115  /* 
 32116   * Operators table
 32117   * This table is sorted by operators priority (highest to lowest) according
 32118   * the JX9 language reference manual.
 32119   * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators.
 32120   * The operators precedence table have been improved dramatically so that you can do same
 32121   * amazing things now such as array dereferencing, on the fly function call, anonymous function
 32122   * as array values, object member access on instantiation and so on.
 32123   * Refer to the following page for a full discussion on these improvements:
 32124   * http://jx9.symisc.net/features.html
 32125   */
 32126  static const jx9_expr_op aOpTable[] = {
 32127  	                              /* Postfix operators */
 32128  	/* Precedence 2(Highest), left-associative */
 32129  	{ {".", sizeof(char)}, EXPR_OP_DOT,     2, EXPR_OP_ASSOC_LEFT ,   JX9_OP_MEMBER },
 32130  	{ {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX}, 
 32131  	/* Precedence 3, non-associative  */
 32132  	{ {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR}, 
 32133  	{ {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR}, 
 32134  	                              /* Unary operators */
 32135  	/* Precedence 4, right-associative  */
 32136  	{ {"-", sizeof(char)},                 EXPR_OP_UMINUS,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS }, 
 32137  	{ {"+", sizeof(char)},                 EXPR_OP_UPLUS,     4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS }, 
 32138  	{ {"~", sizeof(char)},                 EXPR_OP_BITNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT }, 
 32139  	{ {"!", sizeof(char)},                 EXPR_OP_LOGNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT }, 
 32140  	                             /* Cast operators */
 32141  	{ {"(int)",    sizeof("(int)")-1   }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT  }, 
 32142  	{ {"(bool)",   sizeof("(bool)")-1  }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL }, 
 32143  	{ {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR  }, 
 32144  	{ {"(float)",  sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL },
 32145  	{ {"(array)",  sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY },   /* Not used, but reserved for future use */
 32146  	{ {"(object)",  sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
 32147  	                           /* Binary operators */
 32148  	/* Precedence 7, left-associative */ 
 32149  	{ {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL}, 
 32150  	{ {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV}, 
 32151  	{ {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD}, 
 32152  	/* Precedence 8, left-associative */
 32153  	{ {"+", sizeof(char)}, EXPR_OP_ADD, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_ADD}, 
 32154  	{ {"-", sizeof(char)}, EXPR_OP_SUB, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_SUB}, 
 32155  	{ {"..", sizeof(char)*2},EXPR_OP_DDOT, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_CAT}, 
 32156  	/* Precedence 9, left-associative */
 32157  	{ {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL}, 
 32158  	{ {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR}, 
 32159  	/* Precedence 10, non-associative */
 32160  	{ {"<", sizeof(char)},    EXPR_OP_LT,  10, EXPR_OP_NON_ASSOC, JX9_OP_LT}, 
 32161  	{ {">", sizeof(char)},    EXPR_OP_GT,  10, EXPR_OP_NON_ASSOC, JX9_OP_GT}, 
 32162  	{ {"<=", sizeof(char)*2}, EXPR_OP_LE,  10, EXPR_OP_NON_ASSOC, JX9_OP_LE}, 
 32163  	{ {">=", sizeof(char)*2}, EXPR_OP_GE,  10, EXPR_OP_NON_ASSOC, JX9_OP_GE}, 
 32164  	{ {"<>", sizeof(char)*2}, EXPR_OP_NE,  10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, 
 32165  	/* Precedence 11, non-associative */
 32166  	{ {"==", sizeof(char)*2},  EXPR_OP_EQ,  11, EXPR_OP_NON_ASSOC, JX9_OP_EQ}, 
 32167  	{ {"!=", sizeof(char)*2},  EXPR_OP_NE,  11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, 
 32168  	{ {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ}, 
 32169  	{ {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE}, 
 32170  		/* Precedence 12, left-associative */
 32171  	{ {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT,   JX9_OP_BAND}, 
 32172  	                         /* Binary operators */
 32173  	/* Precedence 13, left-associative */
 32174  	{ {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR}, 
 32175  	/* Precedence 14, left-associative */
 32176  	{ {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR}, 
 32177  	/* Precedence 15, left-associative */
 32178  	{ {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND}, 
 32179  	/* Precedence 16, left-associative */
 32180  	{ {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR}, 
 32181  	                      /* Ternary operator */
 32182  	/* Precedence 17, left-associative */
 32183      { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0}, 
 32184  	                     /* Combined binary operators */
 32185  	/* Precedence 18, right-associative */
 32186  	{ {"=", sizeof(char)},     EXPR_OP_ASSIGN,     18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE}, 
 32187  	{ {"+=", sizeof(char)*2},  EXPR_OP_ADD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE }, 
 32188  	{ {"-=", sizeof(char)*2},  EXPR_OP_SUB_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE }, 
 32189  	{ {".=", sizeof(char)*2},  EXPR_OP_DOT_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE }, 
 32190  	{ {"*=", sizeof(char)*2},  EXPR_OP_MUL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE }, 
 32191  	{ {"/=", sizeof(char)*2},  EXPR_OP_DIV_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE }, 
 32192  	{ {"%=", sizeof(char)*2},  EXPR_OP_MOD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE }, 
 32193  	{ {"&=", sizeof(char)*2},  EXPR_OP_AND_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE }, 
 32194  	{ {"|=", sizeof(char)*2},  EXPR_OP_OR_ASSIGN,  18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE  }, 
 32195  	{ {"^=", sizeof(char)*2},  EXPR_OP_XOR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE }, 
 32196  	{ {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE }, 
 32197  	{ {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE },
 32198  		/* Precedence 22, left-associative [Lowest operator] */
 32199  	{ {",", sizeof(char)},  EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */
 32200  };
 32201  /* Function call operator need special handling */
 32202  static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL};
 32203  /*
 32204   * Check if the given token is a potential operator or not.
 32205   * This function is called by the lexer each time it extract a token that may 
 32206   * look like an operator.
 32207   * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success.
 32208   * Otherwise NULL.
 32209   * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
 32210   * a binary minus or unary minus.]
 32211   */
 32212  JX9_PRIVATE const jx9_expr_op *  jx9ExprExtractOperator(SyString *pStr, SyToken *pLast)
 32213  {
 32214  	sxu32 n = 0;
 32215  	sxi32 rc;
 32216  	/* Do a linear lookup on the operators table */
 32217  	for(;;){
 32218  		if( n >= SX_ARRAYSIZE(aOpTable) ){
 32219  			break;
 32220  		}
 32221  		rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);		
 32222  		if( rc == 0 ){
 32223  			if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){
 32224  				if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){
 32225  					/* JSON Array not subscripting, return NULL  */
 32226  					return 0;
 32227  				}
 32228  				/* There is no ambiguity here, simply return the first operator seen */
 32229  				return &aOpTable[n];
 32230  			}
 32231  			/* Handle ambiguity */
 32232  			if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){
 32233  				/* Unary opertors have prcedence here over binary operators */
 32234  				return &aOpTable[n];
 32235  			}
 32236  			if( pLast->nType & JX9_TK_OP ){
 32237  				const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData;
 32238  				/* Ticket 1433-31: Handle the '++', '--' operators case */
 32239  				if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){
 32240  					/* Unary opertors have prcedence here over binary operators */
 32241  					return &aOpTable[n];
 32242  				}
 32243  			
 32244  			}
 32245  		}
 32246  		++n; /* Next operator in the table */
 32247  	}
 32248  	/* No such operator */
 32249  	return 0;
 32250  }
 32251  /*
 32252   * Delimit a set of token stream.
 32253   * This function take care of handling the nesting level and stops when it hit
 32254   * the end of the input or the ending token is found and the nesting level is zero.
 32255   */
 32256  JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd)
 32257  {
 32258  	SyToken *pCur = pIn;
 32259  	sxi32 iNest = 1;
 32260  	for(;;){
 32261  		if( pCur >= pEnd ){
 32262  			break;
 32263  		}
 32264  		if( pCur->nType & nTokStart ){
 32265  			/* Increment nesting level */
 32266  			iNest++;
 32267  		}else if( pCur->nType & nTokEnd ){
 32268  			/* Decrement nesting level */
 32269  			iNest--;
 32270  			if( iNest <= 0 ){
 32271  				break;
 32272  			}
 32273  		}
 32274  		/* Advance cursor */
 32275  		pCur++;
 32276  	}
 32277  	/* Point to the end of the chunk */
 32278  	*ppEnd = pCur;
 32279  }
 32280  /*
 32281   * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise.
 32282   * Note on reserved keywords.
 32283   *  According to the JX9 language reference manual:
 32284   *   These words have special meaning in JX9. Some of them represent things which look like 
 32285   *   functions, some look like constants, and so on--but they're not, really: they are language
 32286   *   constructs. You cannot use any of the following words as constants, object names, function
 32287   *   or method names. Using them as variable names is generally OK, but could lead to confusion.
 32288   */
 32289  JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID)
 32290  {
 32291  	if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE
 32292  		|| nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){
 32293  			return TRUE;
 32294  	}
 32295  	/* Not a language construct */
 32296  	return FALSE; 
 32297  }
 32298  /*
 32299   * Point to the next expression that should be evaluated shortly.
 32300   * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting
 32301   * level is zero.
 32302   */
 32303  JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext)
 32304  {
 32305  	SyToken *pCur = pStart;
 32306  	sxi32 iNest = 0;
 32307  	if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){
 32308  		/* Last expression */
 32309  		return SXERR_EOF;
 32310  	}
 32311  	while( pCur < pEnd ){
 32312  		if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){
 32313  			break;
 32314  		}
 32315  		if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){
 32316  			iNest++;
 32317  		}else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){
 32318  			iNest--;
 32319  		}
 32320  		pCur++;
 32321  	}
 32322  	*ppNext = pCur;
 32323  	return SXRET_OK;
 32324  }
 32325  /*
 32326   * Collect and assemble tokens holding annonymous functions/closure body.
 32327   * When errors, JX9 take care of generating the appropriate error message.
 32328   * Note on annonymous functions.
 32329   *  According to the JX9 language reference manual:
 32330   *  Anonymous functions, also known as closures, allow the creation of functions
 32331   *  which have no specified name. They are most useful as the value of callback
 32332   *  parameters, but they have many other uses. 
 32333   *  Closures may also inherit variables from the parent scope. Any such variables
 32334   *  must be declared in the function header. Inheriting variables from the parent
 32335   *  scope is not the same as using global variables. Global variables exist in the global scope
 32336   *  which is the same no matter what function is executing. The parent scope of a closure is the 
 32337   *  function in which the closure was declared (not necessarily the function it was called from).
 32338   *
 32339   * Some example:
 32340   *  $greet = function($name)
 32341   * {
 32342   *   printf("Hello %s\r\n", $name);
 32343   * };
 32344   *  $greet('World');
 32345   *  $greet('JX9');
 32346   *
 32347   * $double = function($a) {
 32348   *   return $a * 2;
 32349   * };
 32350   * // This is our range of numbers
 32351   * $numbers = range(1, 5);
 32352   * // Use the Annonymous function as a callback here to 
 32353   * // double the size of each element in our 
 32354   * // range
 32355   * $new_numbers = array_map($double, $numbers);
 32356   * print implode(' ', $new_numbers);
 32357   */
 32358  static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd)
 32359  {
 32360  	SyToken *pIn = *ppCur;
 32361  	sxu32 nLine;
 32362  	sxi32 rc;
 32363  	/* Jump the 'function' keyword */
 32364  	nLine = pIn->nLine;
 32365  	pIn++;
 32366  	if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){
 32367  		pIn++;
 32368  	}
 32369  	if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){
 32370  		/* Syntax error */
 32371  		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function");
 32372  		if( rc != SXERR_ABORT ){
 32373  			rc = SXERR_SYNTAX;
 32374  		}
 32375  		goto Synchronize;
 32376  	}
 32377  	pIn++; /* Jump the leading parenthesis '(' */
 32378  	jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn);
 32379  	if( pIn >= pEnd || &pIn[1] >= pEnd ){
 32380  		/* Syntax error */
 32381  		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function");
 32382  		if( rc != SXERR_ABORT ){
 32383  			rc = SXERR_SYNTAX;
 32384  		}
 32385  		goto Synchronize;
 32386  	}
 32387  	pIn++; /* Jump the trailing parenthesis */
 32388  	if( pIn->nType & JX9_TK_OCB /*'{'*/ ){
 32389  		pIn++; /* Jump the leading curly '{' */
 32390  		jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn);
 32391  		if( pIn < pEnd ){
 32392  			pIn++;
 32393  		}
 32394  	}else{
 32395  		/* Syntax error */
 32396  		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'");
 32397  		if( rc == SXERR_ABORT ){
 32398  			return SXERR_ABORT;
 32399  		}			
 32400  	}
 32401  	rc = SXRET_OK;
 32402  Synchronize:
 32403  	/* Synchronize pointers */
 32404  	*ppCur = pIn;
 32405  	return rc;
 32406  }
 32407  /*
 32408   * Make sure we are dealing with a valid expression tree.
 32409   * This function check for balanced parenthesis, braces, brackets and so on.
 32410   * When errors, JX9 take care of generating the appropriate error message.
 32411   * Return SXRET_OK on success. Any other return value indicates syntax error.
 32412   */
 32413  static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode)
 32414  {
 32415  	sxi32 iParen, iSquare, iBraces;
 32416  	sxi32 i, rc;
 32417  
 32418  	if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){
 32419  		/* Fix and mark as an unary not binary plus/minus operator */
 32420  		apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0);
 32421  		apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
 32422  	}
 32423  	iParen = iSquare = iBraces = 0;
 32424  	for( i = 0 ; i < nNode ; ++i ){
 32425  		if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){
 32426  			if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ||
 32427  				(apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){
 32428  					/* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */
 32429  					if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){
 32430  						/* We are dealing with a postfix [i.e: function call]  operator
 32431  						 * not a simple left parenthesis. Mark the node.
 32432  						 */
 32433  						apNode[i]->pStart->nType |= JX9_TK_OP;
 32434  						apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
 32435  						apNode[i]->pOp = &sFCallOp;
 32436  					}
 32437  			}
 32438  			iParen++;
 32439  		}else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){
 32440  			if( iParen <= 0 ){
 32441  				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
 32442  				if( rc != SXERR_ABORT ){
 32443  					rc = SXERR_SYNTAX;
 32444  				}
 32445  				return rc;
 32446  			}
 32447  			iParen--;
 32448  		}else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){
 32449  			iSquare++;
 32450  		}else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){
 32451  			if( iSquare <= 0 ){
 32452  				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
 32453  				if( rc != SXERR_ABORT ){
 32454  					rc = SXERR_SYNTAX;
 32455  				}
 32456  				return rc;
 32457  			}
 32458  			iSquare--;
 32459  		}else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){
 32460  			iBraces++;
 32461  		}else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){
 32462  			if( iBraces <= 0 ){
 32463  				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
 32464  				if( rc != SXERR_ABORT ){
 32465  					rc = SXERR_SYNTAX;
 32466  				}
 32467  				return rc;
 32468  			}
 32469  			iBraces--;
 32470  		}else if( apNode[i]->pStart->nType & JX9_TK_OP ){
 32471  			const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp;
 32472  			if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){
 32473  				if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){
 32474  					sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
 32475  					sxu32 n = 0;
 32476  					if( pOp->iOp == EXPR_OP_UPLUS ){
 32477  						iExprOp = EXPR_OP_ADD; /* Binary plus */
 32478  					}
 32479  					/*
 32480  					 * TICKET 1433-013: This is a fix around an obscure bug when the user uses
 32481  					 * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..].
 32482  					 */
 32483  					while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){
 32484  						++n;
 32485  					}
 32486  					pOp = &aOpTable[n];
 32487  					/* Mark as binary '+' or '-', not an unary */
 32488  					apNode[i]->pOp = pOp;
 32489  					apNode[i]->pStart->pUserData = (void *)pOp;
 32490  				}
 32491  			}
 32492  		}
 32493  	}
 32494  	if( iParen != 0 || iSquare != 0 || iBraces != 0){
 32495  		rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'");
 32496  		if( rc != SXERR_ABORT ){
 32497  			rc = SXERR_SYNTAX;
 32498  		}
 32499  		return rc;
 32500  	}
 32501  	return SXRET_OK;
 32502  }
 32503  /*
 32504   * Extract a single expression node from the input.
 32505   * On success store the freshly extractd node in ppNode.
 32506   * When errors, JX9 take care of generating the appropriate error message.
 32507   * An expression node can be a variable [i.e: $var], an operator [i.e: ++] 
 32508   * an annonymous function [i.e: function(){ return "Hello"; }, a double/single
 32509   * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path
 32510   * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on.
 32511   */
 32512  static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode)
 32513  {
 32514  	jx9_expr_node *pNode;
 32515  	SyToken *pCur;
 32516  	sxi32 rc;
 32517  	/* Allocate a new node */
 32518  	pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node));
 32519  	if( pNode == 0 ){
 32520  		/* If the supplied memory subsystem is so sick that we are unable to allocate
 32521  		 * a tiny chunk of memory, there is no much we can do here.
 32522  		 */
 32523  		return SXERR_MEM;
 32524  	}
 32525  	/* Zero the structure */
 32526  	SyZero(pNode, sizeof(jx9_expr_node));
 32527  	SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **));
 32528  	/* Point to the head of the token stream */
 32529  	pCur = pNode->pStart = pGen->pIn;
 32530  	/* Start collecting tokens */
 32531  	if( pCur->nType & JX9_TK_OP ){
 32532  		/* Point to the instance that describe this operator */
 32533  		pNode->pOp = (const jx9_expr_op *)pCur->pUserData;
 32534  		/* Advance the stream cursor */
 32535  		pCur++;
 32536  	}else if( pCur->nType & JX9_TK_DOLLAR ){
 32537  		/* Isolate variable */
 32538  		pCur++; /* Jump the dollar sign */
 32539  		if( pCur >= pGen->pEnd ){
 32540  			/* Syntax error */
 32541  			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name");
 32542  			if( rc != SXERR_ABORT ){
 32543  				rc = SXERR_SYNTAX;
 32544  			}
 32545  			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32546  			return rc;
 32547  		}
 32548  		pCur++; /* Jump the variable name */
 32549  		pNode->xCode = jx9CompileVariable;
 32550  	}else if( pCur->nType & JX9_TK_OCB /* '{' */ ){
 32551  		/* JSON Object, assemble tokens */
 32552  		pCur++;
 32553  		jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur);
 32554  		if( pCur < pGen->pEnd ){
 32555  			pCur++;
 32556  		}else{
 32557  			/* Syntax error */
 32558  			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'");
 32559  			if( rc != SXERR_ABORT ){
 32560  				rc = SXERR_SYNTAX;
 32561  			}
 32562  			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32563  			return rc;
 32564  		}
 32565  		pNode->xCode = jx9CompileJsonObject;
 32566  	}else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){
 32567  		/* JSON Array, assemble tokens */
 32568  		pCur++;
 32569  		jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur);
 32570  		if( pCur < pGen->pEnd ){
 32571  			pCur++;
 32572  		}else{
 32573  			/* Syntax error */
 32574  			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'");
 32575  			if( rc != SXERR_ABORT ){
 32576  				rc = SXERR_SYNTAX;
 32577  			}
 32578  			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32579  			return rc;
 32580  		}
 32581  		pNode->xCode = jx9CompileJsonArray;
 32582  	}else if( pCur->nType & JX9_TK_KEYWORD ){
 32583  		 int nKeyword = SX_PTR_TO_INT(pCur->pUserData);
 32584  		 if( nKeyword == JX9_TKWRD_FUNCTION ){
 32585  			 /* Annonymous function */
 32586  			  if( &pCur[1] >= pGen->pEnd ){
 32587  				 /* Assume a literal */
 32588  				pCur++;
 32589  				pNode->xCode = jx9CompileLiteral;
 32590  			 }else{
 32591  				 /* Assemble annonymous functions body */
 32592  				 rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd);
 32593  				 if( rc != SXRET_OK ){
 32594  					 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32595  					 return rc; 
 32596  				 }
 32597  				 pNode->xCode = jx9CompileAnnonFunc;
 32598  			  }
 32599  		 }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){
 32600  			 /* Language constructs [i.e: print,die...] require special handling */
 32601  			 jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur);
 32602  			 pNode->xCode = jx9CompileLangConstruct;
 32603  		 }else{
 32604  			 /* Assume a literal */
 32605  			 pCur++;
 32606  			 pNode->xCode = jx9CompileLiteral;
 32607  		 }
 32608  	 }else if( pCur->nType & (JX9_TK_ID) ){
 32609  		 /* Constants, function name, namespace path, object name... */
 32610  		 pCur++;
 32611  		 pNode->xCode = jx9CompileLiteral;
 32612  	 }else{
 32613  		 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 ){
 32614  			 /* Point to the code generator routine */
 32615  			 pNode->xCode = jx9GetNodeHandler(pCur->nType);
 32616  			 if( pNode->xCode == 0 ){
 32617  				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
 32618  				 if( rc != SXERR_ABORT ){
 32619  					 rc = SXERR_SYNTAX;
 32620  				 }
 32621  				 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32622  				 return rc;
 32623  			 }
 32624  		 }
 32625  		/* Advance the stream cursor */
 32626  		pCur++;
 32627  	 }
 32628  	/* Point to the end of the token stream */
 32629  	pNode->pEnd = pCur;
 32630  	/* Save the node for later processing */
 32631  	*ppNode = pNode;
 32632  	/* Synchronize cursors */
 32633  	pGen->pIn = pCur;
 32634  	return SXRET_OK;
 32635  }
 32636  /*
 32637   * Free an expression tree.
 32638   */
 32639  static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode)
 32640  {
 32641  	if( pNode->pLeft ){
 32642  		/* Release the left tree */
 32643  		ExprFreeTree(&(*pGen), pNode->pLeft);
 32644  	}
 32645  	if( pNode->pRight ){
 32646  		/* Release the right tree */
 32647  		ExprFreeTree(&(*pGen), pNode->pRight);
 32648  	}
 32649  	if( pNode->pCond ){
 32650  		/* Release the conditional tree used by the ternary operator */
 32651  		ExprFreeTree(&(*pGen), pNode->pCond);
 32652  	}
 32653  	if( SySetUsed(&pNode->aNodeArgs) > 0 ){
 32654  		jx9_expr_node **apArg;
 32655  		sxu32 n;
 32656  		/* Release node arguments */
 32657  		apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
 32658  		for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){
 32659  			ExprFreeTree(&(*pGen), apArg[n]);
 32660  		}
 32661  		SySetRelease(&pNode->aNodeArgs);
 32662  	}
 32663  	/* Finally, release this node */
 32664  	SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
 32665  }
 32666  /*
 32667   * Free an expression tree.
 32668   * This function is a wrapper around ExprFreeTree() defined above.
 32669   */
 32670  JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet)
 32671  {
 32672  	jx9_expr_node **apNode;
 32673  	sxu32 n;
 32674  	apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet);
 32675  	for( n = 0  ; n < SySetUsed(pNodeSet) ; ++n ){
 32676  		if( apNode[n] ){
 32677  			ExprFreeTree(&(*pGen), apNode[n]);
 32678  		}
 32679  	}
 32680  	return SXRET_OK;
 32681  }
 32682  /*
 32683   * Check if the given node is a modifialbe l/r-value.
 32684   * Return TRUE if modifiable.FALSE otherwise.
 32685   */
 32686  static int ExprIsModifiableValue(jx9_expr_node *pNode)
 32687  {
 32688  	sxi32 iExprOp;
 32689  	if( pNode->pOp == 0 ){
 32690  		return pNode->xCode == jx9CompileVariable ? TRUE : FALSE;
 32691  	}
 32692  	iExprOp = pNode->pOp->iOp;
 32693  	if( iExprOp == EXPR_OP_DOT /*'.' */  ){
 32694  			return TRUE;
 32695  	}
 32696  	if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){
 32697  		if( pNode->pLeft->pOp ) {
 32698  			if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){
 32699  				return FALSE;
 32700  			}
 32701  		}else if( pNode->pLeft->xCode != jx9CompileVariable ){
 32702  			return FALSE;
 32703  		}
 32704  		return TRUE;
 32705  	}
 32706  	/* Not a modifiable l or r-value */
 32707  	return FALSE;
 32708  }
 32709  /* Forward declaration */
 32710  static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken);
 32711  /* Macro to check if the given node is a terminal */
 32712  #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
 32713  /*
 32714   * Buid an expression tree for each given function argument.
 32715   * When errors, JX9 take care of generating the appropriate error message.
 32716   */
 32717  static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken)
 32718  {
 32719  	sxi32 iNest, iCur, iNode;
 32720  	sxi32 rc;
 32721  	/* Process function arguments from left to right */
 32722  	iCur = 0;
 32723  	for(;;){
 32724  		if( iCur >= nToken ){
 32725  			/* No more arguments to process */
 32726  			break;
 32727  		}
 32728  		iNode = iCur;
 32729  		iNest = 0;
 32730  		while( iCur < nToken ){
 32731  			if( apNode[iCur] ){
 32732  				if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){
 32733  					break;
 32734  				}else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){
 32735  					iNest++;
 32736  				}else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){
 32737  					iNest--;
 32738  				}
 32739  			}
 32740  			iCur++;
 32741  		}
 32742  		if( iCur > iNode ){
 32743  			ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode);
 32744  			if( apNode[iNode] ){
 32745  				/* Put a pointer to the root of the tree in the arguments set */
 32746  				SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
 32747  			}else{
 32748  				/* Empty function argument */
 32749  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
 32750  				if( rc != SXERR_ABORT ){
 32751  					rc = SXERR_SYNTAX;
 32752  				}
 32753  				return rc;
 32754  			}
 32755  		}else{
 32756  			rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
 32757  			if( rc != SXERR_ABORT ){
 32758  				rc = SXERR_SYNTAX;
 32759  			}
 32760  			return rc;
 32761  		}
 32762  		/* Jump trailing comma */
 32763  		if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){
 32764  			iCur++;
 32765  			if( iCur >= nToken ){
 32766  				/* missing function argument */
 32767  				rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
 32768  				if( rc != SXERR_ABORT ){
 32769  					rc = SXERR_SYNTAX;
 32770  				}
 32771  				return rc;
 32772  			}
 32773  		}
 32774  	}
 32775  	return SXRET_OK;
 32776  }
 32777  /*
 32778    * Create an expression tree from an array of tokens.
 32779    * If successful, the root of the tree is stored in apNode[0].
 32780    * When errors, JX9 take care of generating the appropriate error message.
 32781    */
 32782   static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken)
 32783   {
 32784  	 sxi32 i, iLeft, iRight;
 32785  	 jx9_expr_node *pNode;
 32786  	 sxi32 iCur;
 32787  	 sxi32 rc;
 32788  	 if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){
 32789  		 /* TICKET 1433-17: self evaluating node */
 32790  		 return SXRET_OK;
 32791  	 }
 32792  	 /* Process expressions enclosed in parenthesis first */
 32793  	 for( iCur =  0 ; iCur < nToken ; ++iCur ){
 32794  		 sxi32 iNest;
 32795  		 /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
 32796  		  * since the LPAREN token can also be an operator [i.e: Function call].
 32797  		  */
 32798  		 if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){
 32799  			 continue;
 32800  		 }
 32801  		 iNest = 1;
 32802  		 iLeft = iCur;
 32803  		 /* Find the closing parenthesis */
 32804  		 iCur++;
 32805  		 while( iCur < nToken ){
 32806  			 if( apNode[iCur] ){
 32807  				 if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){
 32808  					 /* Decrement nesting level */
 32809  					 iNest--;
 32810  					 if( iNest <= 0 ){
 32811  						 break;
 32812  					 }
 32813  				 }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){
 32814  					 /* Increment nesting level */
 32815  					 iNest++;
 32816  				 }
 32817  			 }
 32818  			 iCur++;
 32819  		 }
 32820  		 if( iCur - iLeft > 1 ){
 32821  			 /* Recurse and process this expression */
 32822  			 rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
 32823  			 if( rc != SXRET_OK ){
 32824  				 return rc;
 32825  			 }
 32826  		 }
 32827  		 /* Free the left and right nodes */
 32828  		 ExprFreeTree(&(*pGen), apNode[iLeft]);
 32829  		 ExprFreeTree(&(*pGen), apNode[iCur]);
 32830  		 apNode[iLeft] = 0;
 32831  		 apNode[iCur] = 0;
 32832  	 }
 32833  	 /* Handle postfix [i.e: function call, member access] operators with precedence 2 */
 32834  	 iLeft = -1;
 32835  	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
 32836  		 if( apNode[iCur] == 0 ){
 32837  			 continue;
 32838  		 }
 32839  		 pNode = apNode[iCur];
 32840  		 if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0  ){
 32841  			 if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){
 32842  				 /* Collect function arguments */
 32843  				 sxi32 iPtr = 0;
 32844  				 sxi32 nFuncTok = 0;
 32845  				 while( nFuncTok + iCur < nToken ){
 32846  					 if( apNode[nFuncTok+iCur] ){
 32847  						 if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){
 32848  							 iPtr++;
 32849  						 }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){
 32850  							 iPtr--;
 32851  							 if( iPtr <= 0 ){
 32852  								 break;
 32853  							 }
 32854  						 }
 32855  					 }
 32856  					 nFuncTok++;
 32857  				 }
 32858  				 if( nFuncTok + iCur >= nToken ){
 32859  					 /* Syntax error */
 32860  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
 32861  					 if( rc != SXERR_ABORT ){
 32862  						 rc = SXERR_SYNTAX;
 32863  					 }
 32864  					 return rc; 
 32865  				 }
 32866  				 if(  iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){
 32867  					 /* Syntax error */
 32868  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
 32869  					 if( rc != SXERR_ABORT ){
 32870  						 rc = SXERR_SYNTAX;
 32871  					 }
 32872  					 return rc;
 32873  				 }
 32874  				 if( nFuncTok > 1 ){
 32875  					 /* Process function arguments */
 32876  					 rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1);
 32877  					 if( rc != SXRET_OK ){
 32878  						 return rc;
 32879  					 }
 32880  				 }
 32881  				 /* Link the node to the tree */
 32882  				 pNode->pLeft = apNode[iLeft];
 32883  				 apNode[iLeft] = 0;
 32884  				 for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){
 32885  					 apNode[iCur+iPtr] = 0;
 32886  				 }
 32887  			 }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){
 32888  				 /* Subscripting */
 32889  				 sxi32 iArrTok = iCur + 1;
 32890  				 sxi32 iNest = 1;
 32891  				 if(  iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){
 32892  					 /* Collect index tokens */
 32893  				    while( iArrTok < nToken ){
 32894  					 if( apNode[iArrTok] ){
 32895  						 if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){
 32896  							 /* Increment nesting level */
 32897  							 iNest++;
 32898  						 }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){
 32899  							 /* Decrement nesting level */
 32900  							 iNest--;
 32901  							 if( iNest <= 0 ){
 32902  								 break;
 32903  							 }
 32904  						 }
 32905  					 }
 32906  					 ++iArrTok;
 32907  				   }
 32908  				   if( iArrTok > iCur + 1 ){
 32909  					 /* Recurse and process this expression */
 32910  					 rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1);
 32911  					 if( rc != SXRET_OK ){
 32912  						 return rc;
 32913  					 }
 32914  					 /* Link the node to it's index */
 32915  					 SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]);
 32916  				   }
 32917  				   /* Link the node to the tree */
 32918  				   pNode->pLeft = apNode[iLeft];
 32919  				   pNode->pRight = 0;
 32920  				   apNode[iLeft] = 0;
 32921  				   for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){
 32922  					 apNode[iNest] = 0;
 32923  				  }
 32924  				 }
 32925  			 }else{
 32926  				 /* Member access operators [i.e: '.' ] */
 32927  				 iRight = iCur + 1;
 32928  				 while( iRight < nToken && apNode[iRight] == 0 ){
 32929  					 iRight++;
 32930  				 }
 32931  				 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
 32932  					 /* Syntax error */
 32933  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
 32934  					 if( rc != SXERR_ABORT ){
 32935  						 rc = SXERR_SYNTAX;
 32936  					 }
 32937  					 return rc;
 32938  				 }
 32939  				 /* Link the node to the tree */
 32940  				 pNode->pLeft = apNode[iLeft];
 32941  				 if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){
 32942  					 /* Syntax error */
 32943  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, 
 32944  						 "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
 32945  					 if( rc != SXERR_ABORT ){
 32946  						 rc = SXERR_SYNTAX;
 32947  					 }
 32948  					 return rc;
 32949  				 }
 32950  				 pNode->pRight = apNode[iRight];
 32951  				 apNode[iLeft] = apNode[iRight] = 0;
 32952  			 }
 32953  		 }
 32954  		 iLeft = iCur;
 32955  	 }
 32956  	  /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */
 32957  	 iLeft = -1;
 32958  	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
 32959  		 if( apNode[iCur] == 0 ){
 32960  			 continue;
 32961  		 }
 32962  		 pNode = apNode[iCur];
 32963  		 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
 32964  			 if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
 32965  				 || apNode[iLeft]->xCode == jx9CompileVariable) ){
 32966  					 /* Link the node to the tree */
 32967  					 pNode->pLeft = apNode[iLeft];
 32968  					 apNode[iLeft] = 0; 
 32969  			 }
 32970  		  }
 32971  		 iLeft = iCur;
 32972  	  }
 32973  	 iLeft = -1;
 32974  	 for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
 32975  		 if( apNode[iCur] == 0 ){
 32976  			 continue;
 32977  		 }
 32978  		 pNode = apNode[iCur];
 32979  		 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
 32980  			 if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable)
 32981  				 || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){
 32982  					 /* Syntax error */
 32983  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
 32984  					 if( rc != SXERR_ABORT ){
 32985  						 rc = SXERR_SYNTAX;
 32986  					 }
 32987  					 return rc;
 32988  			 }
 32989  			 /* Link the node to the tree */
 32990  			 pNode->pLeft = apNode[iLeft];
 32991  			 apNode[iLeft] = 0;
 32992  			 /* Mark as pre-increment/decrement node */
 32993  			 pNode->iFlags |= EXPR_NODE_PRE_INCR;
 32994  		  }
 32995  		 iLeft = iCur;
 32996  	 }
 32997  	 /* Handle right associative unary and cast operators [i.e: !, (string), ~...]  with precedence 4 */
 32998  	  iLeft = 0;
 32999  	  for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
 33000  		  if( apNode[iCur] ){
 33001  			  pNode = apNode[iCur];
 33002  			  if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){
 33003  				  if( iLeft > 0 ){
 33004  					  /* Link the node to the tree */
 33005  					  pNode->pLeft = apNode[iLeft];
 33006  					  apNode[iLeft] = 0;
 33007  					  if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){
 33008  						  if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){
 33009  							   /* Syntax error */
 33010  							  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
 33011  							  if( rc != SXERR_ABORT ){
 33012  								  rc = SXERR_SYNTAX;
 33013  							  }
 33014  							  return rc;
 33015  						  }
 33016  					  }
 33017  				  }else{
 33018  					  /* Syntax error */
 33019  					  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
 33020  					  if( rc != SXERR_ABORT ){
 33021  						  rc = SXERR_SYNTAX;
 33022  					  }
 33023  					  return rc;
 33024  				  }
 33025  			  }
 33026  			  /* Save terminal position */
 33027  			  iLeft = iCur;
 33028  		  }
 33029  	  }	 
 33030  	 /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/
 33031  	 for( i = 7 ; i < 17 ; i++ ){
 33032  		 iLeft = -1;
 33033  		 for( iCur = 0 ; iCur < nToken ; ++iCur ){
 33034  			 if( apNode[iCur] == 0 ){
 33035  				 continue;
 33036  			 }
 33037  			 pNode = apNode[iCur];
 33038  			 if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){
 33039  				 /* Get the right node */
 33040  				 iRight = iCur + 1;
 33041  				 while( iRight < nToken && apNode[iRight] == 0 ){
 33042  					 iRight++;
 33043  				 }
 33044  				 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
 33045  					 /* Syntax error */
 33046  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
 33047  					 if( rc != SXERR_ABORT ){
 33048  						 rc = SXERR_SYNTAX;
 33049  					 }
 33050  					 return rc; 
 33051  				 }
 33052  				 /* Link the node to the tree */
 33053  				 pNode->pLeft = apNode[iLeft];
 33054  				 pNode->pRight = apNode[iRight];
 33055  				 apNode[iLeft] = apNode[iRight] = 0;
 33056  			 }
 33057  			 iLeft = iCur;
 33058  		 }
 33059  	 }
 33060  	 /* Handle the ternary operator. (expr1) ? (expr2) : (expr3) 
 33061  	  * Note that we do not need a precedence loop here since
 33062  	  * we are dealing with a single operator.
 33063  	  */
 33064  	  iLeft = -1;
 33065  	  for( iCur = 0 ; iCur < nToken ; ++iCur ){
 33066  		  if( apNode[iCur] == 0 ){
 33067  			  continue;
 33068  		  }
 33069  		  pNode = apNode[iCur];
 33070  		  if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){
 33071  			  sxi32 iNest = 1;
 33072  			  if( iLeft < 0 || !NODE_ISTERM(iLeft) ){
 33073  				  /* Missing condition */
 33074  				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
 33075  				  if( rc != SXERR_ABORT ){
 33076  					  rc = SXERR_SYNTAX;
 33077  				  }
 33078  				  return rc;
 33079  			  }
 33080  			  /* Get the right node */
 33081  			  iRight = iCur + 1;
 33082  			  while( iRight < nToken  ){
 33083  				  if( apNode[iRight] ){
 33084  					  if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){
 33085  						  /* Increment nesting level */
 33086  						  ++iNest;
 33087  					  }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){
 33088  						  /* Decrement nesting level */
 33089  						  --iNest;
 33090  						  if( iNest <= 0 ){
 33091  							  break;
 33092  						  }
 33093  					  }
 33094  				  }
 33095  				  iRight++;
 33096  			  }
 33097  			  if( iRight > iCur + 1 ){
 33098  				  /* Recurse and process the then expression */
 33099  				  rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
 33100  				  if( rc != SXRET_OK ){
 33101  					  return rc;
 33102  				  }
 33103  				  /* Link the node to the tree */
 33104  				  pNode->pLeft = apNode[iCur + 1];
 33105  			  }else{
 33106  				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
 33107  				  if( rc != SXERR_ABORT ){
 33108  					 rc = SXERR_SYNTAX;
 33109  				 }
 33110  				 return rc;
 33111  			  }
 33112  			  apNode[iCur + 1] = 0;
 33113  			  if( iRight + 1 < nToken ){
 33114  				  /* Recurse and process the else expression */
 33115  				  rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
 33116  				  if( rc != SXRET_OK ){
 33117  					  return rc;
 33118  				  }
 33119  				  /* Link the node to the tree */
 33120  				  pNode->pRight = apNode[iRight + 1];
 33121  				  apNode[iRight + 1] =  apNode[iRight] = 0;
 33122  			  }else{
 33123  				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
 33124  				  if( rc != SXERR_ABORT ){
 33125  					 rc = SXERR_SYNTAX;
 33126  				 }
 33127  				 return rc;
 33128  			  }
 33129  			  /* Point to the condition */
 33130  			  pNode->pCond  = apNode[iLeft];
 33131  			  apNode[iLeft] = 0;
 33132  			  break;
 33133  		  }
 33134  		  iLeft = iCur;
 33135  	  }
 33136  	 /* Process right associative binary operators [i.e: '=', '+=', '/='] 
 33137  	  * Note: All right associative binary operators have precedence 18
 33138  	  * so there is no need for a precedence loop here.
 33139  	  */
 33140  	 iRight = -1;
 33141  	 for( iCur = nToken -  1 ; iCur >= 0 ; iCur--){
 33142  		 if( apNode[iCur] == 0 ){
 33143  			 continue;
 33144  		 }
 33145  		 pNode = apNode[iCur];
 33146  		 if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){
 33147  			 /* Get the left node */
 33148  			 iLeft = iCur - 1;
 33149  			 while( iLeft >= 0 && apNode[iLeft] == 0 ){
 33150  				 iLeft--;
 33151  			 }
 33152  			 if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
 33153  				 /* Syntax error */
 33154  				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
 33155  				 if( rc != SXERR_ABORT ){
 33156  					 rc = SXERR_SYNTAX;
 33157  				 }
 33158  				 return rc;
 33159  			 }
 33160  			 if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){
 33161  				 if( pNode->pOp->iVmOp != JX9_OP_STORE  ){
 33162  					 /* Left operand must be a modifiable l-value */
 33163  					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, 
 33164  						 "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
 33165  					 if( rc != SXERR_ABORT ){
 33166  						 rc = SXERR_SYNTAX;
 33167  					 }
 33168  					 return rc; 
 33169  				 }
 33170  			 }
 33171  			 /* Link the node to the tree (Reverse) */
 33172  			 pNode->pLeft = apNode[iRight];
 33173  			 pNode->pRight = apNode[iLeft];
 33174  			 apNode[iLeft] = apNode[iRight] = 0;
 33175  		 }
 33176  		 iRight = iCur;
 33177  	 }
 33178  	 /* Process the lowest precedence operator (22, comma) */
 33179  	 iLeft = -1;
 33180  	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
 33181  		 if( apNode[iCur] == 0 ){
 33182  			 continue;
 33183  		 }
 33184  		 pNode = apNode[iCur];
 33185  		 if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){
 33186  			 /* Get the right node */
 33187  			 iRight = iCur + 1;
 33188  			 while( iRight < nToken && apNode[iRight] == 0 ){
 33189  				 iRight++;
 33190  			 }
 33191  			 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
 33192  				 /* Syntax error */
 33193  				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
 33194  				 if( rc != SXERR_ABORT ){
 33195  					 rc = SXERR_SYNTAX;
 33196  				 }
 33197  				 return rc;
 33198  			 }
 33199  			 /* Link the node to the tree */
 33200  			 pNode->pLeft = apNode[iLeft];
 33201  			 pNode->pRight = apNode[iRight];
 33202  			 apNode[iLeft] = apNode[iRight] = 0;
 33203  		 }
 33204  		 iLeft = iCur;
 33205  	 }
 33206  	 /* Point to the root of the expression tree */
 33207  	 for( iCur = 1 ; iCur < nToken ; ++iCur ){
 33208  		 if( apNode[iCur] ){
 33209  			 if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){
 33210  				 rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
 33211  				  if( rc != SXERR_ABORT ){
 33212  					  rc = SXERR_SYNTAX;
 33213  				  }
 33214  				  return rc;  
 33215  			 }
 33216  			 apNode[0] = apNode[iCur];
 33217  			 apNode[iCur] = 0;
 33218  		 }
 33219  	 }
 33220  	 return SXRET_OK;
 33221   }
 33222   /*
 33223    * Build an expression tree from the freshly extracted raw tokens.
 33224    * If successful, the root of the tree is stored in ppRoot.
 33225    * When errors, JX9 take care of generating the appropriate error message.
 33226    * This is the public interface used by the most code generator routines.
 33227    */
 33228  JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot)
 33229  {
 33230  	jx9_expr_node **apNode;
 33231  	jx9_expr_node *pNode;
 33232  	sxi32 rc;
 33233  	/* Reset node container */
 33234  	SySetReset(pExprNode);
 33235  	pNode = 0; /* Prevent compiler warning */
 33236  	/* Extract nodes one after one until we hit the end of the input */
 33237  	while( pGen->pIn < pGen->pEnd ){
 33238  		rc = ExprExtractNode(&(*pGen), &pNode);
 33239  		if( rc != SXRET_OK ){
 33240  			return rc;
 33241  		}
 33242  		/* Save the extracted node */
 33243  		SySetPut(pExprNode, (const void *)&pNode);
 33244  	}
 33245  	if( SySetUsed(pExprNode) < 1 ){
 33246  		/* Empty expression [i.e: A semi-colon;] */
 33247  		*ppRoot = 0;
 33248  		return SXRET_OK;
 33249  	}
 33250  	apNode = (jx9_expr_node **)SySetBasePtr(pExprNode);
 33251  	/* Make sure we are dealing with valid nodes */
 33252  	rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
 33253  	if( rc != SXRET_OK ){
 33254  		/* Don't worry about freeing memory, upper layer will
 33255  		 * cleanup the mess left behind.
 33256  		 */
 33257  		*ppRoot = 0;
 33258  		return rc;
 33259  	}
 33260  	/* Build the tree */
 33261  	rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
 33262  	if( rc != SXRET_OK ){
 33263  		/* Something goes wrong [i.e: Syntax error] */
 33264  		*ppRoot = 0;
 33265  		return rc;
 33266  	}
 33267  	/* Point to the root of the tree */
 33268  	*ppRoot = apNode[0];
 33269  	return SXRET_OK;
 33270  }
 33271  /*
 33272   * ----------------------------------------------------------
 33273   * File: jx9_vfs.c
 33274   * MD5: 8b73046a366acaf6aa7227c2133e16c0
 33275   * ----------------------------------------------------------
 33276   */
 33277  /*
 33278   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 33279   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 33280   * Version 1.7.2
 33281   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 33282   * please contact Symisc Systems via:
 33283   *       legal@symisc.net
 33284   *       licensing@symisc.net
 33285   *       contact@symisc.net
 33286   * or visit:
 33287   *      http://jx9.symisc.net/
 33288   */
 33289   /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
 33290  #ifndef JX9_AMALGAMATION
 33291  #include "jx9Int.h"
 33292  #endif
 33293  /*
 33294   * This file implement a virtual file systems (VFS) for the JX9 engine.
 33295   */
 33296  /*
 33297   * Given a string containing the path of a file or directory, this function 
 33298   * return the parent directory's path.
 33299   */
 33300  JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen)
 33301  {
 33302  	const char *zEnd = &zPath[nByte - 1];
 33303  	int c, d;
 33304  	c = d = '/';
 33305  #ifdef __WINNT__
 33306  	d = '\\';
 33307  #endif
 33308  	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
 33309  		zEnd--;
 33310  	}
 33311  	*pLen = (int)(zEnd-zPath);
 33312  #ifdef __WINNT__
 33313  	if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){
 33314  		/* Normalize path on windows */
 33315  		return "\\";
 33316  	}
 33317  #endif
 33318  	if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){
 33319  		/* No separator, return "." as the current directory */
 33320  		*pLen = sizeof(char);
 33321  		return ".";
 33322  	}
 33323  	if( (*pLen) == 0 ){
 33324  		*pLen = sizeof(char);
 33325  #ifdef __WINNT__
 33326  		return "\\";
 33327  #else
 33328  		return "/";
 33329  #endif
 33330  	}
 33331  	return zPath;
 33332  }
 33333  /*
 33334   * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined.
 33335   */
 33336  #ifndef JX9_DISABLE_BUILTIN_FUNC
 33337  /*
 33338   * bool chdir(string $directory)
 33339   *  Change the current directory.
 33340   * Parameters
 33341   *  $directory
 33342   *   The new current directory
 33343   * Return
 33344   *  TRUE on success or FALSE on failure.
 33345   */
 33346  static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33347  {
 33348  	const char *zPath;
 33349  	jx9_vfs *pVfs;
 33350  	int rc;
 33351  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33352  		/* Missing/Invalid argument, return FALSE */
 33353  		jx9_result_bool(pCtx, 0);
 33354  		return JX9_OK;
 33355  	}
 33356  	/* Point to the underlying vfs */
 33357  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33358  	if( pVfs == 0 || pVfs->xChdir == 0 ){
 33359  		/* IO routine not implemented, return NULL */
 33360  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33361  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33362  			jx9_function_name(pCtx)
 33363  			);
 33364  		jx9_result_bool(pCtx, 0);
 33365  		return JX9_OK;
 33366  	}
 33367  	/* Point to the desired directory */
 33368  	zPath = jx9_value_to_string(apArg[0], 0);
 33369  	/* Perform the requested operation */
 33370  	rc = pVfs->xChdir(zPath);
 33371  	/* IO return value */
 33372  	jx9_result_bool(pCtx, rc == JX9_OK);
 33373  	return JX9_OK;
 33374  }
 33375  /*
 33376   * bool chroot(string $directory)
 33377   *  Change the root directory.
 33378   * Parameters
 33379   *  $directory
 33380   *   The path to change the root directory to
 33381   * Return
 33382   *  TRUE on success or FALSE on failure.
 33383   */
 33384  static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33385  {
 33386  	const char *zPath;
 33387  	jx9_vfs *pVfs;
 33388  	int rc;
 33389  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33390  		/* Missing/Invalid argument, return FALSE */
 33391  		jx9_result_bool(pCtx, 0);
 33392  		return JX9_OK;
 33393  	}
 33394  	/* Point to the underlying vfs */
 33395  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33396  	if( pVfs == 0 || pVfs->xChroot == 0 ){
 33397  		/* IO routine not implemented, return NULL */
 33398  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33399  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33400  			jx9_function_name(pCtx)
 33401  			);
 33402  		jx9_result_bool(pCtx, 0);
 33403  		return JX9_OK;
 33404  	}
 33405  	/* Point to the desired directory */
 33406  	zPath = jx9_value_to_string(apArg[0], 0);
 33407  	/* Perform the requested operation */
 33408  	rc = pVfs->xChroot(zPath);
 33409  	/* IO return value */
 33410  	jx9_result_bool(pCtx, rc == JX9_OK);
 33411  	return JX9_OK;
 33412  }
 33413  /*
 33414   * string getcwd(void)
 33415   *  Gets the current working directory.
 33416   * Parameters
 33417   *  None
 33418   * Return
 33419   *  Returns the current working directory on success, or FALSE on failure.
 33420   */
 33421  static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33422  {
 33423  	jx9_vfs *pVfs;
 33424  	int rc;
 33425  	/* Point to the underlying vfs */
 33426  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33427  	if( pVfs == 0 || pVfs->xGetcwd == 0 ){
 33428  		SXUNUSED(nArg); /* cc warning */
 33429  		SXUNUSED(apArg);
 33430  		/* IO routine not implemented, return NULL */
 33431  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33432  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33433  			jx9_function_name(pCtx)
 33434  			);
 33435  		jx9_result_bool(pCtx, 0);
 33436  		return JX9_OK;
 33437  	}
 33438  	jx9_result_string(pCtx, "", 0);
 33439  	/* Perform the requested operation */
 33440  	rc = pVfs->xGetcwd(pCtx);
 33441  	if( rc != JX9_OK ){
 33442  		/* Error, return FALSE */
 33443  		jx9_result_bool(pCtx, 0);
 33444  	}
 33445  	return JX9_OK;
 33446  }
 33447  /*
 33448   * bool rmdir(string $directory)
 33449   *  Removes directory.
 33450   * Parameters
 33451   *  $directory
 33452   *   The path to the directory
 33453   * Return
 33454   *  TRUE on success or FALSE on failure.
 33455   */
 33456  static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33457  {
 33458  	const char *zPath;
 33459  	jx9_vfs *pVfs;
 33460  	int rc;
 33461  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33462  		/* Missing/Invalid argument, return FALSE */
 33463  		jx9_result_bool(pCtx, 0);
 33464  		return JX9_OK;
 33465  	}
 33466  	/* Point to the underlying vfs */
 33467  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33468  	if( pVfs == 0 || pVfs->xRmdir == 0 ){
 33469  		/* IO routine not implemented, return NULL */
 33470  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33471  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33472  			jx9_function_name(pCtx)
 33473  			);
 33474  		jx9_result_bool(pCtx, 0);
 33475  		return JX9_OK;
 33476  	}
 33477  	/* Point to the desired directory */
 33478  	zPath = jx9_value_to_string(apArg[0], 0);
 33479  	/* Perform the requested operation */
 33480  	rc = pVfs->xRmdir(zPath);
 33481  	/* IO return value */
 33482  	jx9_result_bool(pCtx, rc == JX9_OK);
 33483  	return JX9_OK;
 33484  }
 33485  /*
 33486   * bool is_dir(string $filename)
 33487   *  Tells whether the given filename is a directory.
 33488   * Parameters
 33489   *  $filename
 33490   *   Path to the file.
 33491   * Return
 33492   *  TRUE on success or FALSE on failure.
 33493   */
 33494  static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33495  {
 33496  	const char *zPath;
 33497  	jx9_vfs *pVfs;
 33498  	int rc;
 33499  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33500  		/* Missing/Invalid argument, return FALSE */
 33501  		jx9_result_bool(pCtx, 0);
 33502  		return JX9_OK;
 33503  	}
 33504  	/* Point to the underlying vfs */
 33505  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33506  	if( pVfs == 0 || pVfs->xIsdir == 0 ){
 33507  		/* IO routine not implemented, return NULL */
 33508  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33509  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33510  			jx9_function_name(pCtx)
 33511  			);
 33512  		jx9_result_bool(pCtx, 0);
 33513  		return JX9_OK;
 33514  	}
 33515  	/* Point to the desired directory */
 33516  	zPath = jx9_value_to_string(apArg[0], 0);
 33517  	/* Perform the requested operation */
 33518  	rc = pVfs->xIsdir(zPath);
 33519  	/* IO return value */
 33520  	jx9_result_bool(pCtx, rc == JX9_OK);
 33521  	return JX9_OK;
 33522  }
 33523  /*
 33524   * bool mkdir(string $pathname[, int $mode = 0777])
 33525   *  Make a directory.
 33526   * Parameters
 33527   *  $pathname
 33528   *   The directory path.
 33529   * $mode
 33530   *  The mode is 0777 by default, which means the widest possible access.
 33531   *  Note:
 33532   *   mode is ignored on Windows.
 33533   *   Note that you probably want to specify the mode as an octal number, which means
 33534   *   it should have a leading zero. The mode is also modified by the current umask
 33535   *   which you can change using umask().
 33536   * Return
 33537   *  TRUE on success or FALSE on failure.
 33538   */
 33539  static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33540  {
 33541  	int iRecursive = 0;
 33542  	const char *zPath;
 33543  	jx9_vfs *pVfs;
 33544  	int iMode, rc;
 33545  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33546  		/* Missing/Invalid argument, return FALSE */
 33547  		jx9_result_bool(pCtx, 0);
 33548  		return JX9_OK;
 33549  	}
 33550  	/* Point to the underlying vfs */
 33551  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33552  	if( pVfs == 0 || pVfs->xMkdir == 0 ){
 33553  		/* IO routine not implemented, return NULL */
 33554  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33555  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33556  			jx9_function_name(pCtx)
 33557  			);
 33558  		jx9_result_bool(pCtx, 0);
 33559  		return JX9_OK;
 33560  	}
 33561  	/* Point to the desired directory */
 33562  	zPath = jx9_value_to_string(apArg[0], 0);
 33563  #ifdef __WINNT__
 33564  	iMode = 0;
 33565  #else
 33566  	/* Assume UNIX */
 33567  	iMode = 0777;
 33568  #endif
 33569  	if( nArg > 1 ){
 33570  		iMode = jx9_value_to_int(apArg[1]);
 33571  		if( nArg > 2 ){
 33572  			iRecursive = jx9_value_to_bool(apArg[2]);
 33573  		}
 33574  	}
 33575  	/* Perform the requested operation */
 33576  	rc = pVfs->xMkdir(zPath, iMode, iRecursive);
 33577  	/* IO return value */
 33578  	jx9_result_bool(pCtx, rc == JX9_OK);
 33579  	return JX9_OK;
 33580  }
 33581  /*
 33582   * bool rename(string $oldname, string $newname)
 33583   *  Attempts to rename oldname to newname.
 33584   * Parameters
 33585   *  $oldname
 33586   *   Old name.
 33587   *  $newname
 33588   *   New name.
 33589   * Return
 33590   *  TRUE on success or FALSE on failure.
 33591   */
 33592  static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33593  {
 33594  	const char *zOld, *zNew;
 33595  	jx9_vfs *pVfs;
 33596  	int rc;
 33597  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 33598  		/* Missing/Invalid arguments, return FALSE */
 33599  		jx9_result_bool(pCtx, 0);
 33600  		return JX9_OK;
 33601  	}
 33602  	/* Point to the underlying vfs */
 33603  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33604  	if( pVfs == 0 || pVfs->xRename == 0 ){
 33605  		/* IO routine not implemented, return NULL */
 33606  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33607  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33608  			jx9_function_name(pCtx)
 33609  			);
 33610  		jx9_result_bool(pCtx, 0);
 33611  		return JX9_OK;
 33612  	}
 33613  	/* Perform the requested operation */
 33614  	zOld = jx9_value_to_string(apArg[0], 0);
 33615  	zNew = jx9_value_to_string(apArg[1], 0);
 33616  	rc = pVfs->xRename(zOld, zNew);
 33617  	/* IO result */
 33618  	jx9_result_bool(pCtx, rc == JX9_OK );
 33619  	return JX9_OK;
 33620  }
 33621  /*
 33622   * string realpath(string $path)
 33623   *  Returns canonicalized absolute pathname.
 33624   * Parameters
 33625   *  $path
 33626   *   Target path.
 33627   * Return
 33628   *  Canonicalized absolute pathname on success. or FALSE on failure.
 33629   */
 33630  static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33631  {
 33632  	const char *zPath;
 33633  	jx9_vfs *pVfs;
 33634          int rc;
 33635  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33636  		/* Missing/Invalid argument, return FALSE */
 33637  		jx9_result_bool(pCtx, 0);
 33638  		return JX9_OK;
 33639  	}
 33640  	/* Point to the underlying vfs */
 33641  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33642  	if( pVfs == 0 || pVfs->xRealpath == 0 ){
 33643  		/* IO routine not implemented, return NULL */
 33644  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33645  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33646  			jx9_function_name(pCtx)
 33647  			);
 33648  		jx9_result_bool(pCtx, 0);
 33649  		return JX9_OK;
 33650  	}
 33651  	/* Set an empty string untnil the underlying OS interface change that */
 33652  	jx9_result_string(pCtx, "", 0);
 33653  	/* Perform the requested operation */
 33654  	zPath = jx9_value_to_string(apArg[0], 0);
 33655  	rc = pVfs->xRealpath(zPath, pCtx);
 33656  	if( rc != JX9_OK ){
 33657  	 jx9_result_bool(pCtx, 0);
 33658  	}
 33659  	return JX9_OK;
 33660  }
 33661  /*
 33662   * int sleep(int $seconds)
 33663   *  Delays the program execution for the given number of seconds.
 33664   * Parameters
 33665   *  $seconds
 33666   *   Halt time in seconds.
 33667   * Return
 33668   *  Zero on success or FALSE on failure.
 33669   */
 33670  static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33671  {
 33672  	jx9_vfs *pVfs;
 33673  	int rc, nSleep;
 33674  	if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
 33675  		/* Missing/Invalid argument, return FALSE */
 33676  		jx9_result_bool(pCtx, 0);
 33677  		return JX9_OK;
 33678  	}
 33679  	/* Point to the underlying vfs */
 33680  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33681  	if( pVfs == 0 || pVfs->xSleep == 0 ){
 33682  		/* IO routine not implemented, return NULL */
 33683  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33684  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33685  			jx9_function_name(pCtx)
 33686  			);
 33687  		jx9_result_bool(pCtx, 0);
 33688  		return JX9_OK;
 33689  	}
 33690  	/* Amount to sleep */
 33691  	nSleep = jx9_value_to_int(apArg[0]);
 33692  	if( nSleep < 0 ){
 33693  		/* Invalid value, return FALSE */
 33694  		jx9_result_bool(pCtx, 0);
 33695  		return JX9_OK;
 33696  	}
 33697  	/* Perform the requested operation (Microseconds) */
 33698  	rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
 33699  	if( rc != JX9_OK ){
 33700  		/* Return FALSE */
 33701  		jx9_result_bool(pCtx, 0);
 33702  	}else{
 33703  		/* Return zero */
 33704  		jx9_result_int(pCtx, 0);
 33705  	}
 33706  	return JX9_OK;
 33707  }
 33708  /*
 33709   * void usleep(int $micro_seconds)
 33710   *  Delays program execution for the given number of micro seconds.
 33711   * Parameters
 33712   *  $micro_seconds
 33713   *   Halt time in micro seconds. A micro second is one millionth of a second.
 33714   * Return
 33715   *  None.
 33716   */
 33717  static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33718  {
 33719  	jx9_vfs *pVfs;
 33720  	int nSleep;
 33721  	if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
 33722  		/* Missing/Invalid argument, return immediately */
 33723  		return JX9_OK;
 33724  	}
 33725  	/* Point to the underlying vfs */
 33726  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33727  	if( pVfs == 0 || pVfs->xSleep == 0 ){
 33728  		/* IO routine not implemented, return NULL */
 33729  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33730  			"IO routine(%s) not implemented in the underlying VFS", 
 33731  			jx9_function_name(pCtx)
 33732  			);
 33733  		return JX9_OK;
 33734  	}
 33735  	/* Amount to sleep */
 33736  	nSleep = jx9_value_to_int(apArg[0]);
 33737  	if( nSleep < 0 ){
 33738  		/* Invalid value, return immediately */
 33739  		return JX9_OK;
 33740  	}
 33741  	/* Perform the requested operation (Microseconds) */
 33742  	pVfs->xSleep((unsigned int)nSleep);
 33743  	return JX9_OK;
 33744  }
 33745  /*
 33746   * bool unlink (string $filename)
 33747   *  Delete a file.
 33748   * Parameters
 33749   *  $filename
 33750   *   Path to the file.
 33751   * Return
 33752   *  TRUE on success or FALSE on failure.
 33753   */
 33754  static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33755  {
 33756  	const char *zPath;
 33757  	jx9_vfs *pVfs;
 33758  	int rc;
 33759  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33760  		/* Missing/Invalid argument, return FALSE */
 33761  		jx9_result_bool(pCtx, 0);
 33762  		return JX9_OK;
 33763  	}
 33764  	/* Point to the underlying vfs */
 33765  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33766  	if( pVfs == 0 || pVfs->xUnlink == 0 ){
 33767  		/* IO routine not implemented, return NULL */
 33768  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33769  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33770  			jx9_function_name(pCtx)
 33771  			);
 33772  		jx9_result_bool(pCtx, 0);
 33773  		return JX9_OK;
 33774  	}
 33775  	/* Point to the desired directory */
 33776  	zPath = jx9_value_to_string(apArg[0], 0);
 33777  	/* Perform the requested operation */
 33778  	rc = pVfs->xUnlink(zPath);
 33779  	/* IO return value */
 33780  	jx9_result_bool(pCtx, rc == JX9_OK);
 33781  	return JX9_OK;
 33782  }
 33783  /*
 33784   * bool chmod(string $filename, int $mode)
 33785   *  Attempts to change the mode of the specified file to that given in mode.
 33786   * Parameters
 33787   *  $filename
 33788   *   Path to the file.
 33789   * $mode
 33790   *   Mode (Must be an integer)
 33791   * Return
 33792   *  TRUE on success or FALSE on failure.
 33793   */
 33794  static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33795  {
 33796  	const char *zPath;
 33797  	jx9_vfs *pVfs;
 33798  	int iMode;
 33799  	int rc;
 33800  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
 33801  		/* Missing/Invalid argument, return FALSE */
 33802  		jx9_result_bool(pCtx, 0);
 33803  		return JX9_OK;
 33804  	}
 33805  	/* Point to the underlying vfs */
 33806  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33807  	if( pVfs == 0 || pVfs->xChmod == 0 ){
 33808  		/* IO routine not implemented, return NULL */
 33809  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33810  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33811  			jx9_function_name(pCtx)
 33812  			);
 33813  		jx9_result_bool(pCtx, 0);
 33814  		return JX9_OK;
 33815  	}
 33816  	/* Point to the desired directory */
 33817  	zPath = jx9_value_to_string(apArg[0], 0);
 33818  	/* Extract the mode */
 33819  	iMode = jx9_value_to_int(apArg[1]);
 33820  	/* Perform the requested operation */
 33821  	rc = pVfs->xChmod(zPath, iMode);
 33822  	/* IO return value */
 33823  	jx9_result_bool(pCtx, rc == JX9_OK);
 33824  	return JX9_OK;
 33825  }
 33826  /*
 33827   * bool chown(string $filename, string $user)
 33828   *  Attempts to change the owner of the file filename to user user.
 33829   * Parameters
 33830   *  $filename
 33831   *   Path to the file.
 33832   * $user
 33833   *   Username.
 33834   * Return
 33835   *  TRUE on success or FALSE on failure.
 33836   */
 33837  static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33838  {
 33839  	const char *zPath, *zUser;
 33840  	jx9_vfs *pVfs;
 33841  	int rc;
 33842  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
 33843  		/* Missing/Invalid arguments, return FALSE */
 33844  		jx9_result_bool(pCtx, 0);
 33845  		return JX9_OK;
 33846  	}
 33847  	/* Point to the underlying vfs */
 33848  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33849  	if( pVfs == 0 || pVfs->xChown == 0 ){
 33850  		/* IO routine not implemented, return NULL */
 33851  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33852  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33853  			jx9_function_name(pCtx)
 33854  			);
 33855  		jx9_result_bool(pCtx, 0);
 33856  		return JX9_OK;
 33857  	}
 33858  	/* Point to the desired directory */
 33859  	zPath = jx9_value_to_string(apArg[0], 0);
 33860  	/* Extract the user */
 33861  	zUser = jx9_value_to_string(apArg[1], 0);
 33862  	/* Perform the requested operation */
 33863  	rc = pVfs->xChown(zPath, zUser);
 33864  	/* IO return value */
 33865  	jx9_result_bool(pCtx, rc == JX9_OK);
 33866  	return JX9_OK;
 33867  }
 33868  /*
 33869   * bool chgrp(string $filename, string $group)
 33870   *  Attempts to change the group of the file filename to group.
 33871   * Parameters
 33872   *  $filename
 33873   *   Path to the file.
 33874   * $group
 33875   *   groupname.
 33876   * Return
 33877   *  TRUE on success or FALSE on failure.
 33878   */
 33879  static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33880  {
 33881  	const char *zPath, *zGroup;
 33882  	jx9_vfs *pVfs;
 33883  	int rc;
 33884  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
 33885  		/* Missing/Invalid arguments, return FALSE */
 33886  		jx9_result_bool(pCtx, 0);
 33887  		return JX9_OK;
 33888  	}
 33889  	/* Point to the underlying vfs */
 33890  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33891  	if( pVfs == 0 || pVfs->xChgrp == 0 ){
 33892  		/* IO routine not implemented, return NULL */
 33893  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33894  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33895  			jx9_function_name(pCtx)
 33896  			);
 33897  		jx9_result_bool(pCtx, 0);
 33898  		return JX9_OK;
 33899  	}
 33900  	/* Point to the desired directory */
 33901  	zPath = jx9_value_to_string(apArg[0], 0);
 33902  	/* Extract the user */
 33903  	zGroup = jx9_value_to_string(apArg[1], 0);
 33904  	/* Perform the requested operation */
 33905  	rc = pVfs->xChgrp(zPath, zGroup);
 33906  	/* IO return value */
 33907  	jx9_result_bool(pCtx, rc == JX9_OK);
 33908  	return JX9_OK;
 33909  }
 33910  /*
 33911   * int64 disk_free_space(string $directory)
 33912   *  Returns available space on filesystem or disk partition.
 33913   * Parameters
 33914   *  $directory
 33915   *   A directory of the filesystem or disk partition.
 33916   * Return
 33917   *  Returns the number of available bytes as a 64-bit integer or FALSE on failure.
 33918   */
 33919  static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33920  {
 33921  	const char *zPath;
 33922  	jx9_int64 iSize;
 33923  	jx9_vfs *pVfs;
 33924  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33925  		/* Missing/Invalid argument, return FALSE */
 33926  		jx9_result_bool(pCtx, 0);
 33927  		return JX9_OK;
 33928  	}
 33929  	/* Point to the underlying vfs */
 33930  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33931  	if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
 33932  		/* IO routine not implemented, return NULL */
 33933  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33934  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33935  			jx9_function_name(pCtx)
 33936  			);
 33937  		jx9_result_bool(pCtx, 0);
 33938  		return JX9_OK;
 33939  	}
 33940  	/* Point to the desired directory */
 33941  	zPath = jx9_value_to_string(apArg[0], 0);
 33942  	/* Perform the requested operation */
 33943  	iSize = pVfs->xFreeSpace(zPath);
 33944  	/* IO return value */
 33945  	jx9_result_int64(pCtx, iSize);
 33946  	return JX9_OK;
 33947  }
 33948  /*
 33949   * int64 disk_total_space(string $directory)
 33950   *  Returns the total size of a filesystem or disk partition.
 33951   * Parameters
 33952   *  $directory
 33953   *   A directory of the filesystem or disk partition.
 33954   * Return
 33955   *  Returns the number of available bytes as a 64-bit integer or FALSE on failure.
 33956   */
 33957  static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33958  {
 33959  	const char *zPath;
 33960  	jx9_int64 iSize;
 33961  	jx9_vfs *pVfs;
 33962  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 33963  		/* Missing/Invalid argument, return FALSE */
 33964  		jx9_result_bool(pCtx, 0);
 33965  		return JX9_OK;
 33966  	}
 33967  	/* Point to the underlying vfs */
 33968  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 33969  	if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
 33970  		/* IO routine not implemented, return NULL */
 33971  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 33972  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 33973  			jx9_function_name(pCtx)
 33974  			);
 33975  		jx9_result_bool(pCtx, 0);
 33976  		return JX9_OK;
 33977  	}
 33978  	/* Point to the desired directory */
 33979  	zPath = jx9_value_to_string(apArg[0], 0);
 33980  	/* Perform the requested operation */
 33981  	iSize = pVfs->xTotalSpace(zPath);
 33982  	/* IO return value */
 33983  	jx9_result_int64(pCtx, iSize);
 33984  	return JX9_OK;
 33985  }
 33986  /*
 33987   * bool file_exists(string $filename)
 33988   *  Checks whether a file or directory exists.
 33989   * Parameters
 33990   *  $filename
 33991   *   Path to the file.
 33992   * Return
 33993   *  TRUE on success or FALSE on failure.
 33994   */
 33995  static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
 33996  {
 33997  	const char *zPath;
 33998  	jx9_vfs *pVfs;
 33999  	int rc;
 34000  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34001  		/* Missing/Invalid argument, return FALSE */
 34002  		jx9_result_bool(pCtx, 0);
 34003  		return JX9_OK;
 34004  	}
 34005  	/* Point to the underlying vfs */
 34006  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34007  	if( pVfs == 0 || pVfs->xFileExists == 0 ){
 34008  		/* IO routine not implemented, return NULL */
 34009  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34010  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34011  			jx9_function_name(pCtx)
 34012  			);
 34013  		jx9_result_bool(pCtx, 0);
 34014  		return JX9_OK;
 34015  	}
 34016  	/* Point to the desired directory */
 34017  	zPath = jx9_value_to_string(apArg[0], 0);
 34018  	/* Perform the requested operation */
 34019  	rc = pVfs->xFileExists(zPath);
 34020  	/* IO return value */
 34021  	jx9_result_bool(pCtx, rc == JX9_OK);
 34022  	return JX9_OK;
 34023  }
 34024  /*
 34025   * int64 file_size(string $filename)
 34026   *  Gets the size for the given file.
 34027   * Parameters
 34028   *  $filename
 34029   *   Path to the file.
 34030   * Return
 34031   *  File size on success or FALSE on failure.
 34032   */
 34033  static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34034  {
 34035  	const char *zPath;
 34036  	jx9_int64 iSize;
 34037  	jx9_vfs *pVfs;
 34038  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34039  		/* Missing/Invalid argument, return FALSE */
 34040  		jx9_result_bool(pCtx, 0);
 34041  		return JX9_OK;
 34042  	}
 34043  	/* Point to the underlying vfs */
 34044  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34045  	if( pVfs == 0 || pVfs->xFileSize == 0 ){
 34046  		/* IO routine not implemented, return NULL */
 34047  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34048  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34049  			jx9_function_name(pCtx)
 34050  			);
 34051  		jx9_result_bool(pCtx, 0);
 34052  		return JX9_OK;
 34053  	}
 34054  	/* Point to the desired directory */
 34055  	zPath = jx9_value_to_string(apArg[0], 0);
 34056  	/* Perform the requested operation */
 34057  	iSize = pVfs->xFileSize(zPath);
 34058  	/* IO return value */
 34059  	jx9_result_int64(pCtx, iSize);
 34060  	return JX9_OK;
 34061  }
 34062  /*
 34063   * int64 fileatime(string $filename)
 34064   *  Gets the last access time of the given file.
 34065   * Parameters
 34066   *  $filename
 34067   *   Path to the file.
 34068   * Return
 34069   *  File atime on success or FALSE on failure.
 34070   */
 34071  static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34072  {
 34073  	const char *zPath;
 34074  	jx9_int64 iTime;
 34075  	jx9_vfs *pVfs;
 34076  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34077  		/* Missing/Invalid argument, return FALSE */
 34078  		jx9_result_bool(pCtx, 0);
 34079  		return JX9_OK;
 34080  	}
 34081  	/* Point to the underlying vfs */
 34082  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34083  	if( pVfs == 0 || pVfs->xFileAtime == 0 ){
 34084  		/* IO routine not implemented, return NULL */
 34085  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34086  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34087  			jx9_function_name(pCtx)
 34088  			);
 34089  		jx9_result_bool(pCtx, 0);
 34090  		return JX9_OK;
 34091  	}
 34092  	/* Point to the desired directory */
 34093  	zPath = jx9_value_to_string(apArg[0], 0);
 34094  	/* Perform the requested operation */
 34095  	iTime = pVfs->xFileAtime(zPath);
 34096  	/* IO return value */
 34097  	jx9_result_int64(pCtx, iTime);
 34098  	return JX9_OK;
 34099  }
 34100  /*
 34101   * int64 filemtime(string $filename)
 34102   *  Gets file modification time.
 34103   * Parameters
 34104   *  $filename
 34105   *   Path to the file.
 34106   * Return
 34107   *  File mtime on success or FALSE on failure.
 34108   */
 34109  static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34110  {
 34111  	const char *zPath;
 34112  	jx9_int64 iTime;
 34113  	jx9_vfs *pVfs;
 34114  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34115  		/* Missing/Invalid argument, return FALSE */
 34116  		jx9_result_bool(pCtx, 0);
 34117  		return JX9_OK;
 34118  	}
 34119  	/* Point to the underlying vfs */
 34120  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34121  	if( pVfs == 0 || pVfs->xFileMtime == 0 ){
 34122  		/* IO routine not implemented, return NULL */
 34123  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34124  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34125  			jx9_function_name(pCtx)
 34126  			);
 34127  		jx9_result_bool(pCtx, 0);
 34128  		return JX9_OK;
 34129  	}
 34130  	/* Point to the desired directory */
 34131  	zPath = jx9_value_to_string(apArg[0], 0);
 34132  	/* Perform the requested operation */
 34133  	iTime = pVfs->xFileMtime(zPath);
 34134  	/* IO return value */
 34135  	jx9_result_int64(pCtx, iTime);
 34136  	return JX9_OK;
 34137  }
 34138  /*
 34139   * int64 filectime(string $filename)
 34140   *  Gets inode change time of file.
 34141   * Parameters
 34142   *  $filename
 34143   *   Path to the file.
 34144   * Return
 34145   *  File ctime on success or FALSE on failure.
 34146   */
 34147  static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34148  {
 34149  	const char *zPath;
 34150  	jx9_int64 iTime;
 34151  	jx9_vfs *pVfs;
 34152  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34153  		/* Missing/Invalid argument, return FALSE */
 34154  		jx9_result_bool(pCtx, 0);
 34155  		return JX9_OK;
 34156  	}
 34157  	/* Point to the underlying vfs */
 34158  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34159  	if( pVfs == 0 || pVfs->xFileCtime == 0 ){
 34160  		/* IO routine not implemented, return NULL */
 34161  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34162  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34163  			jx9_function_name(pCtx)
 34164  			);
 34165  		jx9_result_bool(pCtx, 0);
 34166  		return JX9_OK;
 34167  	}
 34168  	/* Point to the desired directory */
 34169  	zPath = jx9_value_to_string(apArg[0], 0);
 34170  	/* Perform the requested operation */
 34171  	iTime = pVfs->xFileCtime(zPath);
 34172  	/* IO return value */
 34173  	jx9_result_int64(pCtx, iTime);
 34174  	return JX9_OK;
 34175  }
 34176  /*
 34177   * bool is_file(string $filename)
 34178   *  Tells whether the filename is a regular file.
 34179   * Parameters
 34180   *  $filename
 34181   *   Path to the file.
 34182   * Return
 34183   *  TRUE on success or FALSE on failure.
 34184   */
 34185  static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34186  {
 34187  	const char *zPath;
 34188  	jx9_vfs *pVfs;
 34189  	int rc;
 34190  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34191  		/* Missing/Invalid argument, return FALSE */
 34192  		jx9_result_bool(pCtx, 0);
 34193  		return JX9_OK;
 34194  	}
 34195  	/* Point to the underlying vfs */
 34196  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34197  	if( pVfs == 0 || pVfs->xIsfile == 0 ){
 34198  		/* IO routine not implemented, return NULL */
 34199  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34200  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34201  			jx9_function_name(pCtx)
 34202  			);
 34203  		jx9_result_bool(pCtx, 0);
 34204  		return JX9_OK;
 34205  	}
 34206  	/* Point to the desired directory */
 34207  	zPath = jx9_value_to_string(apArg[0], 0);
 34208  	/* Perform the requested operation */
 34209  	rc = pVfs->xIsfile(zPath);
 34210  	/* IO return value */
 34211  	jx9_result_bool(pCtx, rc == JX9_OK);
 34212  	return JX9_OK;
 34213  }
 34214  /*
 34215   * bool is_link(string $filename)
 34216   *  Tells whether the filename is a symbolic link.
 34217   * Parameters
 34218   *  $filename
 34219   *   Path to the file.
 34220   * Return
 34221   *  TRUE on success or FALSE on failure.
 34222   */
 34223  static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34224  {
 34225  	const char *zPath;
 34226  	jx9_vfs *pVfs;
 34227  	int rc;
 34228  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34229  		/* Missing/Invalid argument, return FALSE */
 34230  		jx9_result_bool(pCtx, 0);
 34231  		return JX9_OK;
 34232  	}
 34233  	/* Point to the underlying vfs */
 34234  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34235  	if( pVfs == 0 || pVfs->xIslink == 0 ){
 34236  		/* IO routine not implemented, return NULL */
 34237  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34238  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34239  			jx9_function_name(pCtx)
 34240  			);
 34241  		jx9_result_bool(pCtx, 0);
 34242  		return JX9_OK;
 34243  	}
 34244  	/* Point to the desired directory */
 34245  	zPath = jx9_value_to_string(apArg[0], 0);
 34246  	/* Perform the requested operation */
 34247  	rc = pVfs->xIslink(zPath);
 34248  	/* IO return value */
 34249  	jx9_result_bool(pCtx, rc == JX9_OK);
 34250  	return JX9_OK;
 34251  }
 34252  /*
 34253   * bool is_readable(string $filename)
 34254   *  Tells whether a file exists and is readable.
 34255   * Parameters
 34256   *  $filename
 34257   *   Path to the file.
 34258   * Return
 34259   *  TRUE on success or FALSE on failure.
 34260   */
 34261  static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34262  {
 34263  	const char *zPath;
 34264  	jx9_vfs *pVfs;
 34265  	int rc;
 34266  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34267  		/* Missing/Invalid argument, return FALSE */
 34268  		jx9_result_bool(pCtx, 0);
 34269  		return JX9_OK;
 34270  	}
 34271  	/* Point to the underlying vfs */
 34272  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34273  	if( pVfs == 0 || pVfs->xReadable == 0 ){
 34274  		/* IO routine not implemented, return NULL */
 34275  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34276  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34277  			jx9_function_name(pCtx)
 34278  			);
 34279  		jx9_result_bool(pCtx, 0);
 34280  		return JX9_OK;
 34281  	}
 34282  	/* Point to the desired directory */
 34283  	zPath = jx9_value_to_string(apArg[0], 0);
 34284  	/* Perform the requested operation */
 34285  	rc = pVfs->xReadable(zPath);
 34286  	/* IO return value */
 34287  	jx9_result_bool(pCtx, rc == JX9_OK);
 34288  	return JX9_OK;
 34289  }
 34290  /*
 34291   * bool is_writable(string $filename)
 34292   *  Tells whether the filename is writable.
 34293   * Parameters
 34294   *  $filename
 34295   *   Path to the file.
 34296   * Return
 34297   *  TRUE on success or FALSE on failure.
 34298   */
 34299  static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34300  {
 34301  	const char *zPath;
 34302  	jx9_vfs *pVfs;
 34303  	int rc;
 34304  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34305  		/* Missing/Invalid argument, return FALSE */
 34306  		jx9_result_bool(pCtx, 0);
 34307  		return JX9_OK;
 34308  	}
 34309  	/* Point to the underlying vfs */
 34310  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34311  	if( pVfs == 0 || pVfs->xWritable == 0 ){
 34312  		/* IO routine not implemented, return NULL */
 34313  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34314  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34315  			jx9_function_name(pCtx)
 34316  			);
 34317  		jx9_result_bool(pCtx, 0);
 34318  		return JX9_OK;
 34319  	}
 34320  	/* Point to the desired directory */
 34321  	zPath = jx9_value_to_string(apArg[0], 0);
 34322  	/* Perform the requested operation */
 34323  	rc = pVfs->xWritable(zPath);
 34324  	/* IO return value */
 34325  	jx9_result_bool(pCtx, rc == JX9_OK);
 34326  	return JX9_OK;
 34327  }
 34328  /*
 34329   * bool is_executable(string $filename)
 34330   *  Tells whether the filename is executable.
 34331   * Parameters
 34332   *  $filename
 34333   *   Path to the file.
 34334   * Return
 34335   *  TRUE on success or FALSE on failure.
 34336   */
 34337  static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34338  {
 34339  	const char *zPath;
 34340  	jx9_vfs *pVfs;
 34341  	int rc;
 34342  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34343  		/* Missing/Invalid argument, return FALSE */
 34344  		jx9_result_bool(pCtx, 0);
 34345  		return JX9_OK;
 34346  	}
 34347  	/* Point to the underlying vfs */
 34348  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34349  	if( pVfs == 0 || pVfs->xExecutable == 0 ){
 34350  		/* IO routine not implemented, return NULL */
 34351  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34352  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34353  			jx9_function_name(pCtx)
 34354  			);
 34355  		jx9_result_bool(pCtx, 0);
 34356  		return JX9_OK;
 34357  	}
 34358  	/* Point to the desired directory */
 34359  	zPath = jx9_value_to_string(apArg[0], 0);
 34360  	/* Perform the requested operation */
 34361  	rc = pVfs->xExecutable(zPath);
 34362  	/* IO return value */
 34363  	jx9_result_bool(pCtx, rc == JX9_OK);
 34364  	return JX9_OK;
 34365  }
 34366  /*
 34367   * string filetype(string $filename)
 34368   *  Gets file type.
 34369   * Parameters
 34370   *  $filename
 34371   *   Path to the file.
 34372   * Return
 34373   *  The type of the file. Possible values are fifo, char, dir, block, link
 34374   *  file, socket and unknown.
 34375   */
 34376  static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34377  {
 34378  	const char *zPath;
 34379  	jx9_vfs *pVfs;
 34380  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34381  		/* Missing/Invalid argument, return 'unknown' */
 34382  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 34383  		return JX9_OK;
 34384  	}
 34385  	/* Point to the underlying vfs */
 34386  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34387  	if( pVfs == 0 || pVfs->xFiletype == 0 ){
 34388  		/* IO routine not implemented, return NULL */
 34389  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34390  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34391  			jx9_function_name(pCtx)
 34392  			);
 34393  		jx9_result_bool(pCtx, 0);
 34394  		return JX9_OK;
 34395  	}
 34396  	/* Point to the desired directory */
 34397  	zPath = jx9_value_to_string(apArg[0], 0);
 34398  	/* Set the empty string as the default return value */
 34399  	jx9_result_string(pCtx, "", 0);
 34400  	/* Perform the requested operation */
 34401  	pVfs->xFiletype(zPath, pCtx);
 34402  	return JX9_OK;
 34403  }
 34404  /*
 34405   * array stat(string $filename)
 34406   *  Gives information about a file.
 34407   * Parameters
 34408   *  $filename
 34409   *   Path to the file.
 34410   * Return
 34411   *  An associative array on success holding the following entries on success
 34412   *  0   dev     device number
 34413   * 1    ino     inode number (zero on windows)
 34414   * 2    mode    inode protection mode
 34415   * 3    nlink   number of links
 34416   * 4    uid     userid of owner (zero on windows)
 34417   * 5    gid     groupid of owner (zero on windows)
 34418   * 6    rdev    device type, if inode device
 34419   * 7    size    size in bytes
 34420   * 8    atime   time of last access (Unix timestamp)
 34421   * 9    mtime   time of last modification (Unix timestamp)
 34422   * 10   ctime   time of last inode change (Unix timestamp)
 34423   * 11   blksize blocksize of filesystem IO (zero on windows)
 34424   * 12   blocks  number of 512-byte blocks allocated.
 34425   * Note:
 34426   *  FALSE is returned on failure.
 34427   */
 34428  static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34429  {
 34430  	jx9_value *pArray, *pValue;
 34431  	const char *zPath;
 34432  	jx9_vfs *pVfs;
 34433  	int rc;
 34434  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34435  		/* Missing/Invalid argument, return FALSE */
 34436  		jx9_result_bool(pCtx, 0);
 34437  		return JX9_OK;
 34438  	}
 34439  	/* Point to the underlying vfs */
 34440  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34441  	if( pVfs == 0 || pVfs->xStat == 0 ){
 34442  		/* IO routine not implemented, return NULL */
 34443  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34444  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34445  			jx9_function_name(pCtx)
 34446  			);
 34447  		jx9_result_bool(pCtx, 0);
 34448  		return JX9_OK;
 34449  	}
 34450  	/* Create the array and the working value */
 34451  	pArray = jx9_context_new_array(pCtx);
 34452  	pValue = jx9_context_new_scalar(pCtx);
 34453  	if( pArray == 0 || pValue == 0 ){
 34454  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 34455  		jx9_result_bool(pCtx, 0);
 34456  		return JX9_OK;
 34457  	}
 34458  	/* Extract the file path */
 34459  	zPath = jx9_value_to_string(apArg[0], 0);
 34460  	/* Perform the requested operation */
 34461  	rc = pVfs->xStat(zPath, pArray, pValue);
 34462  	if( rc != JX9_OK ){
 34463  		/* IO error, return FALSE */
 34464  		jx9_result_bool(pCtx, 0);
 34465  	}else{
 34466  		/* Return the associative array */
 34467  		jx9_result_value(pCtx, pArray);
 34468  	}
 34469  	/* Don't worry about freeing memory here, everything will be released
 34470  	 * automatically as soon we return from this function. */
 34471  	return JX9_OK;
 34472  }
 34473  /*
 34474   * array lstat(string $filename)
 34475   *  Gives information about a file or symbolic link.
 34476   * Parameters
 34477   *  $filename
 34478   *   Path to the file.
 34479   * Return
 34480   *  An associative array on success holding the following entries on success
 34481   *  0   dev     device number
 34482   * 1    ino     inode number (zero on windows)
 34483   * 2    mode    inode protection mode
 34484   * 3    nlink   number of links
 34485   * 4    uid     userid of owner (zero on windows)
 34486   * 5    gid     groupid of owner (zero on windows)
 34487   * 6    rdev    device type, if inode device
 34488   * 7    size    size in bytes
 34489   * 8    atime   time of last access (Unix timestamp)
 34490   * 9    mtime   time of last modification (Unix timestamp)
 34491   * 10   ctime   time of last inode change (Unix timestamp)
 34492   * 11   blksize blocksize of filesystem IO (zero on windows)
 34493   * 12   blocks  number of 512-byte blocks allocated.
 34494   * Note:
 34495   *  FALSE is returned on failure.
 34496   */
 34497  static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34498  {
 34499  	jx9_value *pArray, *pValue;
 34500  	const char *zPath;
 34501  	jx9_vfs *pVfs;
 34502  	int rc;
 34503  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34504  		/* Missing/Invalid argument, return FALSE */
 34505  		jx9_result_bool(pCtx, 0);
 34506  		return JX9_OK;
 34507  	}
 34508  	/* Point to the underlying vfs */
 34509  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34510  	if( pVfs == 0 || pVfs->xlStat == 0 ){
 34511  		/* IO routine not implemented, return NULL */
 34512  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34513  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34514  			jx9_function_name(pCtx)
 34515  			);
 34516  		jx9_result_bool(pCtx, 0);
 34517  		return JX9_OK;
 34518  	}
 34519  	/* Create the array and the working value */
 34520  	pArray = jx9_context_new_array(pCtx);
 34521  	pValue = jx9_context_new_scalar(pCtx);
 34522  	if( pArray == 0 || pValue == 0 ){
 34523  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 34524  		jx9_result_bool(pCtx, 0);
 34525  		return JX9_OK;
 34526  	}
 34527  	/* Extract the file path */
 34528  	zPath = jx9_value_to_string(apArg[0], 0);
 34529  	/* Perform the requested operation */
 34530  	rc = pVfs->xlStat(zPath, pArray, pValue);
 34531  	if( rc != JX9_OK ){
 34532  		/* IO error, return FALSE */
 34533  		jx9_result_bool(pCtx, 0);
 34534  	}else{
 34535  		/* Return the associative array */
 34536  		jx9_result_value(pCtx, pArray);
 34537  	}
 34538  	/* Don't worry about freeing memory here, everything will be released
 34539  	 * automatically as soon we return from this function. */
 34540  	return JX9_OK;
 34541  }
 34542  /*
 34543   * string getenv(string $varname)
 34544   *  Gets the value of an environment variable.
 34545   * Parameters
 34546   *  $varname
 34547   *   The variable name.
 34548   * Return
 34549   *  Returns the value of the environment variable varname, or FALSE if the environment
 34550   * variable varname does not exist. 
 34551   */
 34552  static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34553  {
 34554  	const char *zEnv;
 34555  	jx9_vfs *pVfs;
 34556  	int iLen;
 34557  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34558  		/* Missing/Invalid argument, return FALSE */
 34559  		jx9_result_bool(pCtx, 0);
 34560  		return JX9_OK;
 34561  	}
 34562  	/* Point to the underlying vfs */
 34563  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34564  	if( pVfs == 0 || pVfs->xGetenv == 0 ){
 34565  		/* IO routine not implemented, return NULL */
 34566  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34567  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34568  			jx9_function_name(pCtx)
 34569  			);
 34570  		jx9_result_bool(pCtx, 0);
 34571  		return JX9_OK;
 34572  	}
 34573  	/* Extract the environment variable */
 34574  	zEnv = jx9_value_to_string(apArg[0], &iLen);
 34575  	/* Set a boolean FALSE as the default return value */
 34576  	jx9_result_bool(pCtx, 0);
 34577  	if( iLen < 1 ){
 34578  		/* Empty string */
 34579  		return JX9_OK;
 34580  	}
 34581  	/* Perform the requested operation */
 34582  	pVfs->xGetenv(zEnv, pCtx);
 34583  	return JX9_OK;
 34584  }
 34585  /*
 34586   * bool putenv(string $settings)
 34587   *  Set the value of an environment variable.
 34588   * Parameters
 34589   *  $setting
 34590   *   The setting, like "FOO=BAR"
 34591   * Return
 34592   *  TRUE on success or FALSE on failure.  
 34593   */
 34594  static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34595  {
 34596  	const char *zName, *zValue;
 34597  	char *zSettings, *zEnd;
 34598  	jx9_vfs *pVfs;
 34599  	int iLen, rc;
 34600  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34601  		/* Missing/Invalid argument, return FALSE */
 34602  		jx9_result_bool(pCtx, 0);
 34603  		return JX9_OK;
 34604  	}
 34605  	/* Extract the setting variable */
 34606  	zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
 34607  	if( iLen < 1 ){
 34608  		/* Empty string, return FALSE */
 34609  		jx9_result_bool(pCtx, 0);
 34610  		return JX9_OK;
 34611  	}
 34612  	/* Parse the setting */
 34613  	zEnd = &zSettings[iLen];
 34614  	zValue = 0;
 34615  	zName = zSettings;
 34616  	while( zSettings < zEnd ){
 34617  		if( zSettings[0] == '=' ){
 34618  			/* Null terminate the name */
 34619  			zSettings[0] = 0;
 34620  			zValue = &zSettings[1];
 34621  			break;
 34622  		}
 34623  		zSettings++;
 34624  	}
 34625  	/* Install the environment variable in the $_Env array */
 34626  	if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){
 34627  		/* Invalid settings, retun FALSE */
 34628  		jx9_result_bool(pCtx, 0);
 34629  		if( zSettings  < zEnd ){
 34630  			zSettings[0] = '=';
 34631  		}
 34632  		return JX9_OK;
 34633  	}
 34634  	jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
 34635  	/* Point to the underlying vfs */
 34636  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34637  	if( pVfs == 0 || pVfs->xSetenv == 0 ){
 34638  		/* IO routine not implemented, return NULL */
 34639  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34640  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34641  			jx9_function_name(pCtx)
 34642  			);
 34643  		jx9_result_bool(pCtx, 0);
 34644  		zSettings[0] = '=';
 34645  		return JX9_OK;
 34646  	}
 34647  	/* Perform the requested operation */
 34648  	rc = pVfs->xSetenv(zName, zValue);
 34649  	jx9_result_bool(pCtx, rc == JX9_OK );
 34650  	zSettings[0] = '=';
 34651  	return JX9_OK;
 34652  }
 34653  /*
 34654   * bool touch(string $filename[, int64 $time = time()[, int64 $atime]])
 34655   *  Sets access and modification time of file.
 34656   * Note: On windows
 34657   *   If the file does not exists, it will not be created.
 34658   * Parameters
 34659   *  $filename
 34660   *   The name of the file being touched.
 34661   *  $time
 34662   *   The touch time. If time is not supplied, the current system time is used.
 34663   * $atime
 34664   *   If present, the access time of the given filename is set to the value of atime.
 34665   *   Otherwise, it is set to the value passed to the time parameter. If neither are 
 34666   *   present, the current system time is used.
 34667   * Return
 34668   *  TRUE on success or FALSE on failure.
 34669  */
 34670  static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34671  {
 34672  	jx9_int64 nTime, nAccess;
 34673  	const char *zFile;
 34674  	jx9_vfs *pVfs;
 34675  	int rc;
 34676  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34677  		/* Missing/Invalid argument, return FALSE */
 34678  		jx9_result_bool(pCtx, 0);
 34679  		return JX9_OK;
 34680  	}
 34681  	/* Point to the underlying vfs */
 34682  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 34683  	if( pVfs == 0 || pVfs->xTouch == 0 ){
 34684  		/* IO routine not implemented, return NULL */
 34685  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 34686  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 34687  			jx9_function_name(pCtx)
 34688  			);
 34689  		jx9_result_bool(pCtx, 0);
 34690  		return JX9_OK;
 34691  	}
 34692  	/* Perform the requested operation */
 34693  	nTime = nAccess = -1;
 34694  	zFile = jx9_value_to_string(apArg[0], 0);
 34695  	if( nArg > 1 ){
 34696  		nTime = jx9_value_to_int64(apArg[1]);
 34697  		if( nArg > 2 ){
 34698  			nAccess = jx9_value_to_int64(apArg[1]);
 34699  		}else{
 34700  			nAccess = nTime;
 34701  		}
 34702  	}
 34703  	rc = pVfs->xTouch(zFile, nTime, nAccess);
 34704  	/* IO result */
 34705  	jx9_result_bool(pCtx, rc == JX9_OK);
 34706  	return JX9_OK;
 34707  }
 34708  /*
 34709   * Path processing functions that do not need access to the VFS layer
 34710   * Authors:
 34711   *    Symisc Systems, devel@symisc.net.
 34712   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 34713   * Status:
 34714   *    Stable.
 34715   */
 34716  /*
 34717   * string dirname(string $path)
 34718   *  Returns parent directory's path.
 34719   * Parameters
 34720   * $path
 34721   *  Target path.
 34722   *  On Windows, both slash (/) and backslash (\) are used as directory separator character.
 34723   *  In other environments, it is the forward slash (/).
 34724   * Return
 34725   *  The path of the parent directory. If there are no slashes in path, a dot ('.') 
 34726   *  is returned, indicating the current directory.
 34727   */
 34728  static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34729  {
 34730  	const char *zPath, *zDir;
 34731  	int iLen, iDirlen;
 34732  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34733  		/* Missing/Invalid arguments, return the empty string */
 34734  		jx9_result_string(pCtx, "", 0);
 34735  		return JX9_OK;
 34736  	}
 34737  	/* Point to the target path */
 34738  	zPath = jx9_value_to_string(apArg[0], &iLen);
 34739  	if( iLen < 1 ){
 34740  		/* Reuturn "." */
 34741  		jx9_result_string(pCtx, ".", sizeof(char));
 34742  		return JX9_OK;
 34743  	}
 34744  	/* Perform the requested operation */
 34745  	zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
 34746  	/* Return directory name */
 34747  	jx9_result_string(pCtx, zDir, iDirlen);
 34748  	return JX9_OK;
 34749  }
 34750  /*
 34751   * string basename(string $path[, string $suffix ])
 34752   *  Returns trailing name component of path.
 34753   * Parameters
 34754   * $path
 34755   *  Target path.
 34756   *  On Windows, both slash (/) and backslash (\) are used as directory separator character.
 34757   *  In other environments, it is the forward slash (/).
 34758   * $suffix
 34759   *  If the name component ends in suffix this will also be cut off.
 34760   * Return
 34761   *  The base name of the given path. 
 34762   */
 34763  static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34764  {
 34765  	const char *zPath, *zBase, *zEnd;
 34766  	int c, d, iLen;
 34767  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34768  		/* Missing/Invalid argument, return the empty string */
 34769  		jx9_result_string(pCtx, "", 0);
 34770  		return JX9_OK;
 34771  	}
 34772  	c = d = '/';
 34773  #ifdef __WINNT__
 34774  	d = '\\';
 34775  #endif
 34776  	/* Point to the target path */
 34777  	zPath = jx9_value_to_string(apArg[0], &iLen);
 34778  	if( iLen < 1 ){
 34779  		/* Empty string */
 34780  		jx9_result_string(pCtx, "", 0);
 34781  		return JX9_OK;
 34782  	}
 34783  	/* Perform the requested operation */
 34784  	zEnd = &zPath[iLen - 1];
 34785  	/* Ignore trailing '/' */
 34786  	while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){
 34787  		zEnd--;
 34788  	}
 34789  	iLen = (int)(&zEnd[1]-zPath);
 34790  	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
 34791  		zEnd--;
 34792  	}
 34793  	zBase = (zEnd > zPath) ? &zEnd[1] : zPath;
 34794  	zEnd = &zPath[iLen];
 34795  	if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
 34796  		const char *zSuffix;
 34797  		int nSuffix;
 34798  		/* Strip suffix */
 34799  		zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
 34800  		if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
 34801  			zEnd -= nSuffix;
 34802  		}
 34803  	}
 34804  	/* Store the basename */
 34805  	jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
 34806  	return JX9_OK;
 34807  }
 34808  /*
 34809   * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
 34810   *  Returns information about a file path.
 34811   * Parameter
 34812   *  $path
 34813   *   The path to be parsed.
 34814   *  $options
 34815   *    If present, specifies a specific element to be returned; one of
 34816   *      PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME.
 34817   * Return
 34818   *  If the options parameter is not passed, an associative array containing the following
 34819   *  elements is returned: dirname, basename, extension (if any), and filename. 
 34820   *  If options is present, returns a string containing the requested element. 
 34821   */
 34822  typedef struct path_info path_info;
 34823  struct path_info
 34824  {
 34825  	SyString sDir; /* Directory [i.e: /var/www] */
 34826  	SyString sBasename; /* Basename [i.e httpd.conf] */
 34827  	SyString sExtension; /* File extension [i.e xml, pdf..] */
 34828  	SyString sFilename;  /* Filename */
 34829  };
 34830  /*
 34831   * Extract path fields.
 34832   */
 34833  static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut)
 34834  {
 34835  	const char *zPtr, *zEnd = &zPath[nByte - 1];
 34836  	SyString *pCur;
 34837  	int c, d;
 34838  	c = d = '/';
 34839  #ifdef __WINNT__
 34840  	d = '\\';
 34841  #endif
 34842  	/* Zero the structure */
 34843  	SyZero(pOut, sizeof(path_info));
 34844  	/* Handle special case */
 34845  	if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){
 34846  #ifdef __WINNT__
 34847  		SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
 34848  #else
 34849  		SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
 34850  #endif
 34851  		return SXRET_OK;
 34852  	}
 34853  	/* Extract the basename */
 34854  	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
 34855  		zEnd--;
 34856  	}
 34857  	zPtr = (zEnd > zPath) ? &zEnd[1] : zPath;
 34858  	zEnd = &zPath[nByte];
 34859  	/* dirname */
 34860  	pCur = &pOut->sDir;
 34861  	SyStringInitFromBuf(pCur, zPath, zPtr-zPath);
 34862  	if( pCur->nByte > 1 ){
 34863  		SyStringTrimTrailingChar(pCur, '/');
 34864  #ifdef __WINNT__
 34865  		SyStringTrimTrailingChar(pCur, '\\');
 34866  #endif
 34867  	}else if( (int)zPath[0] == c || (int)zPath[0] == d ){
 34868  #ifdef __WINNT__
 34869  		SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
 34870  #else
 34871  		SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
 34872  #endif
 34873  	}
 34874  	/* basename/filename */
 34875  	pCur = &pOut->sBasename;
 34876  	SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr);
 34877  	SyStringTrimLeadingChar(pCur, '/');
 34878  #ifdef __WINNT__
 34879  	SyStringTrimLeadingChar(pCur, '\\');
 34880  #endif
 34881  	SyStringDupPtr(&pOut->sFilename, pCur);
 34882  	if( pCur->nByte > 0 ){
 34883  		/* extension */
 34884  		zEnd--;
 34885  		while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){
 34886  			zEnd--;
 34887  		}
 34888  		if( zEnd > pCur->zString ){
 34889  			zEnd++; /* Jump leading dot */
 34890  			SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd);
 34891  			/* Fix filename */
 34892  			pCur = &pOut->sFilename;
 34893  			if( pCur->nByte > SyStringLength(&pOut->sExtension) ){
 34894  				pCur->nByte -= 1 + SyStringLength(&pOut->sExtension);
 34895  			}
 34896  		}
 34897  	}
 34898  	return SXRET_OK;
 34899  }
 34900  /*
 34901   * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
 34902   *  See block comment above.
 34903   */
 34904  static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
 34905  {
 34906  	const char *zPath;
 34907  	path_info sInfo;
 34908  	SyString *pComp;
 34909  	int iLen;
 34910  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 34911  		/* Missing/Invalid argument, return the empty string */
 34912  		jx9_result_string(pCtx, "", 0);
 34913  		return JX9_OK;
 34914  	}
 34915  	/* Point to the target path */
 34916  	zPath = jx9_value_to_string(apArg[0], &iLen);
 34917  	if( iLen < 1 ){
 34918  		/* Empty string */
 34919  		jx9_result_string(pCtx, "", 0);
 34920  		return JX9_OK;
 34921  	}
 34922  	/* Extract path info */
 34923  	ExtractPathInfo(zPath, iLen, &sInfo);
 34924  	if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
 34925  		/* Return path component */
 34926  		int nComp = jx9_value_to_int(apArg[1]);
 34927  		switch(nComp){
 34928  		case 1: /* PATHINFO_DIRNAME */
 34929  			pComp = &sInfo.sDir;
 34930  			if( pComp->nByte > 0 ){
 34931  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 34932  			}else{
 34933  				/* Expand the empty string */
 34934  				jx9_result_string(pCtx, "", 0);
 34935  			}
 34936  			break;
 34937  		case 2: /*PATHINFO_BASENAME*/
 34938  			pComp = &sInfo.sBasename;
 34939  			if( pComp->nByte > 0 ){
 34940  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 34941  			}else{
 34942  				/* Expand the empty string */
 34943  				jx9_result_string(pCtx, "", 0);
 34944  			}
 34945  			break;
 34946  		case 3: /*PATHINFO_EXTENSION*/
 34947  			pComp = &sInfo.sExtension;
 34948  			if( pComp->nByte > 0 ){
 34949  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 34950  			}else{
 34951  				/* Expand the empty string */
 34952  				jx9_result_string(pCtx, "", 0);
 34953  			}
 34954  			break;
 34955  		case 4: /*PATHINFO_FILENAME*/
 34956  			pComp = &sInfo.sFilename;
 34957  			if( pComp->nByte > 0 ){
 34958  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 34959  			}else{
 34960  				/* Expand the empty string */
 34961  				jx9_result_string(pCtx, "", 0);
 34962  			}
 34963  			break;
 34964  		default:
 34965  			/* Expand the empty string */
 34966  			jx9_result_string(pCtx, "", 0);
 34967  			break;
 34968  		}
 34969  	}else{
 34970  		/* Return an associative array */
 34971  		jx9_value *pArray, *pValue;
 34972  		pArray = jx9_context_new_array(pCtx);
 34973  		pValue = jx9_context_new_scalar(pCtx);
 34974  		if( pArray == 0 || pValue == 0 ){
 34975  			/* Out of mem, return NULL */
 34976  			jx9_result_bool(pCtx, 0);
 34977  			return JX9_OK;
 34978  		}
 34979  		/* dirname */
 34980  		pComp = &sInfo.sDir;
 34981  		if( pComp->nByte > 0 ){
 34982  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 34983  			/* Perform the insertion */
 34984  			jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
 34985  		}
 34986  		/* Reset the string cursor */
 34987  		jx9_value_reset_string_cursor(pValue);
 34988  		/* basername */
 34989  		pComp = &sInfo.sBasename;
 34990  		if( pComp->nByte > 0 ){
 34991  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 34992  			/* Perform the insertion */
 34993  			jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
 34994  		}
 34995  		/* Reset the string cursor */
 34996  		jx9_value_reset_string_cursor(pValue);
 34997  		/* extension */
 34998  		pComp = &sInfo.sExtension;
 34999  		if( pComp->nByte > 0 ){
 35000  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 35001  			/* Perform the insertion */
 35002  			jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
 35003  		}
 35004  		/* Reset the string cursor */
 35005  		jx9_value_reset_string_cursor(pValue);
 35006  		/* filename */
 35007  		pComp = &sInfo.sFilename;
 35008  		if( pComp->nByte > 0 ){
 35009  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 35010  			/* Perform the insertion */
 35011  			jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
 35012  		}
 35013  		/* Return the created array */
 35014  		jx9_result_value(pCtx, pArray);
 35015  		/* Don't worry about freeing memory, everything will be released
 35016  		 * automatically as soon we return from this foreign function.
 35017  		 */
 35018  	}
 35019  	return JX9_OK;
 35020  }
 35021  /*
 35022   * Globbing implementation extracted from the sqlite3 source tree.
 35023   * Original author: D. Richard Hipp (http://www.sqlite.org)
 35024   * Status: Public Domain
 35025   */
 35026  typedef unsigned char u8;
 35027  /* An array to map all upper-case characters into their corresponding
 35028  ** lower-case character. 
 35029  **
 35030  ** SQLite only considers US-ASCII (or EBCDIC) characters.  We do not
 35031  ** handle case conversions for the UTF character set since the tables
 35032  ** involved are nearly as big or bigger than SQLite itself.
 35033  */
 35034  static const unsigned char sqlite3UpperToLower[] = {
 35035        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 
 35036       18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 
 35037       36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 
 35038       54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 
 35039      104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 
 35040      122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 
 35041      108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 
 35042      126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 
 35043      144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 
 35044      162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 
 35045      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 
 35046      198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 
 35047      216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 
 35048      234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 
 35049      252, 253, 254, 255
 35050  };
 35051  #define GlogUpperToLower(A)     if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
 35052  /*
 35053  ** Assuming zIn points to the first byte of a UTF-8 character, 
 35054  ** advance zIn to point to the first byte of the next UTF-8 character.
 35055  */
 35056  #define SQLITE_SKIP_UTF8(zIn) {                        \
 35057    if( (*(zIn++))>=0xc0 ){                              \
 35058      while( (*zIn & 0xc0)==0x80 ){ zIn++; }             \
 35059    }                                                    \
 35060  }
 35061  /*
 35062  ** Compare two UTF-8 strings for equality where the first string can
 35063  ** potentially be a "glob" expression.  Return true (1) if they
 35064  ** are the same and false (0) if they are different.
 35065  **
 35066  ** Globbing rules:
 35067  **
 35068  **      '*'       Matches any sequence of zero or more characters.
 35069  **
 35070  **      '?'       Matches exactly one character.
 35071  **
 35072  **     [...]      Matches one character from the enclosed list of
 35073  **                characters.
 35074  **
 35075  **     [^...]     Matches one character not in the enclosed list.
 35076  **
 35077  ** With the [...] and [^...] matching, a ']' character can be included
 35078  ** in the list by making it the first character after '[' or '^'.  A
 35079  ** range of characters can be specified using '-'.  Example:
 35080  ** "[a-z]" matches any single lower-case letter.  To match a '-', make
 35081  ** it the last character in the list.
 35082  **
 35083  ** This routine is usually quick, but can be N**2 in the worst case.
 35084  **
 35085  ** Hints: to match '*' or '?', put them in "[]".  Like this:
 35086  **
 35087  **         abc[*]xyz        Matches "abc*xyz" only
 35088  */
 35089  static int patternCompare(
 35090    const u8 *zPattern,              /* The glob pattern */
 35091    const u8 *zString,               /* The string to compare against the glob */
 35092    const int esc,                    /* The escape character */
 35093    int noCase
 35094  ){
 35095    int c, c2;
 35096    int invert;
 35097    int seen;
 35098    u8 matchOne = '?';
 35099    u8 matchAll = '*';
 35100    u8 matchSet = '[';
 35101    int prevEscape = 0;     /* True if the previous character was 'escape' */
 35102  
 35103    if( !zPattern || !zString ) return 0;
 35104    while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
 35105      if( !prevEscape && c==matchAll ){
 35106        while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
 35107                 || c == matchOne ){
 35108          if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
 35109            return 0;
 35110          }
 35111        }
 35112        if( c==0 ){
 35113          return 1;
 35114        }else if( c==esc ){
 35115          c = jx9Utf8Read(zPattern, 0, &zPattern);
 35116          if( c==0 ){
 35117            return 0;
 35118          }
 35119        }else if( c==matchSet ){
 35120  	  if( (esc==0) || (matchSet<0x80) ) return 0;
 35121  	  while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){
 35122            SQLITE_SKIP_UTF8(zString);
 35123          }
 35124          return *zString!=0;
 35125        }
 35126        while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){
 35127          if( noCase ){
 35128            GlogUpperToLower(c2);
 35129            GlogUpperToLower(c);
 35130            while( c2 != 0 && c2 != c ){
 35131              c2 = jx9Utf8Read(zString, 0, &zString);
 35132              GlogUpperToLower(c2);
 35133            }
 35134          }else{
 35135            while( c2 != 0 && c2 != c ){
 35136              c2 = jx9Utf8Read(zString, 0, &zString);
 35137            }
 35138          }
 35139          if( c2==0 ) return 0;
 35140  		if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
 35141        }
 35142        return 0;
 35143      }else if( !prevEscape && c==matchOne ){
 35144        if( jx9Utf8Read(zString, 0, &zString)==0 ){
 35145          return 0;
 35146        }
 35147      }else if( c==matchSet ){
 35148        int prior_c = 0;
 35149        if( esc == 0 ) return 0;
 35150        seen = 0;
 35151        invert = 0;
 35152        c = jx9Utf8Read(zString, 0, &zString);
 35153        if( c==0 ) return 0;
 35154        c2 = jx9Utf8Read(zPattern, 0, &zPattern);
 35155        if( c2=='^' ){
 35156          invert = 1;
 35157          c2 = jx9Utf8Read(zPattern, 0, &zPattern);
 35158        }
 35159        if( c2==']' ){
 35160          if( c==']' ) seen = 1;
 35161          c2 = jx9Utf8Read(zPattern, 0, &zPattern);
 35162        }
 35163        while( c2 && c2!=']' ){
 35164          if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
 35165            c2 = jx9Utf8Read(zPattern, 0, &zPattern);
 35166            if( c>=prior_c && c<=c2 ) seen = 1;
 35167            prior_c = 0;
 35168          }else{
 35169            if( c==c2 ){
 35170              seen = 1;
 35171            }
 35172            prior_c = c2;
 35173          }
 35174          c2 = jx9Utf8Read(zPattern, 0, &zPattern);
 35175        }
 35176        if( c2==0 || (seen ^ invert)==0 ){
 35177          return 0;
 35178        }
 35179      }else if( esc==c && !prevEscape ){
 35180        prevEscape = 1;
 35181      }else{
 35182        c2 = jx9Utf8Read(zString, 0, &zString);
 35183        if( noCase ){
 35184          GlogUpperToLower(c);
 35185          GlogUpperToLower(c2);
 35186        }
 35187        if( c!=c2 ){
 35188          return 0;
 35189        }
 35190        prevEscape = 0;
 35191      }
 35192    }
 35193    return *zString==0;
 35194  }
 35195  /*
 35196   * Wrapper around patternCompare() defined above.
 35197   * See block comment above for more information.
 35198   */
 35199  static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare)
 35200  {
 35201  	int rc;
 35202  	if( iEsc < 0 ){
 35203  		iEsc = '\\';
 35204  	}
 35205  	rc = patternCompare(zPattern, zString, iEsc, CaseCompare);
 35206  	return rc;
 35207  }
 35208  /*
 35209   * bool fnmatch(string $pattern, string $string[, int $flags = 0 ])
 35210   *  Match filename against a pattern.
 35211   * Parameters
 35212   *  $pattern
 35213   *   The shell wildcard pattern.
 35214   * $string
 35215   *  The tested string.
 35216   * $flags
 35217   *   A list of possible flags:
 35218   *    FNM_NOESCAPE 	Disable backslash escaping.
 35219   *    FNM_PATHNAME 	Slash in string only matches slash in the given pattern.
 35220   *    FNM_PERIOD 	Leading period in string must be exactly matched by period in the given pattern.
 35221   *    FNM_CASEFOLD 	Caseless match.
 35222   * Return
 35223   *  TRUE if there is a match, FALSE otherwise. 
 35224   */
 35225  static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35226  {
 35227  	const char *zString, *zPattern;
 35228  	int iEsc = '\\';
 35229  	int noCase = 0;
 35230  	int rc;
 35231  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 35232  		/* Missing/Invalid arguments, return FALSE */
 35233  		jx9_result_bool(pCtx, 0);
 35234  		return JX9_OK;
 35235  	}
 35236  	/* Extract the pattern and the string */
 35237  	zPattern  = jx9_value_to_string(apArg[0], 0);
 35238  	zString = jx9_value_to_string(apArg[1], 0);
 35239  	/* Extract the flags if avaialble */
 35240  	if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
 35241  		rc = jx9_value_to_int(apArg[2]);
 35242  		if( rc & 0x01 /*FNM_NOESCAPE*/){
 35243  			iEsc = 0;
 35244  		}
 35245  		if( rc & 0x08 /*FNM_CASEFOLD*/){
 35246  			noCase = 1;
 35247  		}
 35248  	}
 35249  	/* Go globbing */
 35250  	rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase);
 35251  	/* Globbing result */
 35252  	jx9_result_bool(pCtx, rc);
 35253  	return JX9_OK;
 35254  }
 35255  /*
 35256   * bool strglob(string $pattern, string $string)
 35257   *  Match string against a pattern.
 35258   * Parameters
 35259   *  $pattern
 35260   *   The shell wildcard pattern.
 35261   * $string
 35262   *  The tested string.
 35263   * Return
 35264   *  TRUE if there is a match, FALSE otherwise. 
 35265   */
 35266  static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35267  {
 35268  	const char *zString, *zPattern;
 35269  	int iEsc = '\\';
 35270  	int rc;
 35271  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 35272  		/* Missing/Invalid arguments, return FALSE */
 35273  		jx9_result_bool(pCtx, 0);
 35274  		return JX9_OK;
 35275  	}
 35276  	/* Extract the pattern and the string */
 35277  	zPattern  = jx9_value_to_string(apArg[0], 0);
 35278  	zString = jx9_value_to_string(apArg[1], 0);
 35279  	/* Go globbing */
 35280  	rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
 35281  	/* Globbing result */
 35282  	jx9_result_bool(pCtx, rc);
 35283  	return JX9_OK;
 35284  }
 35285  /*
 35286   * bool link(string $target, string $link)
 35287   *  Create a hard link.
 35288   * Parameters
 35289   *  $target
 35290   *   Target of the link.
 35291   *  $link
 35292   *   The link name.
 35293   * Return
 35294   *  TRUE on success or FALSE on failure.
 35295   */
 35296  static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35297  {
 35298  	const char *zTarget, *zLink;
 35299  	jx9_vfs *pVfs;
 35300  	int rc;
 35301  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 35302  		/* Missing/Invalid arguments, return FALSE */
 35303  		jx9_result_bool(pCtx, 0);
 35304  		return JX9_OK;
 35305  	}
 35306  	/* Point to the underlying vfs */
 35307  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35308  	if( pVfs == 0 || pVfs->xLink == 0 ){
 35309  		/* IO routine not implemented, return NULL */
 35310  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35311  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 35312  			jx9_function_name(pCtx)
 35313  			);
 35314  		jx9_result_bool(pCtx, 0);
 35315  		return JX9_OK;
 35316  	}
 35317  	/* Extract the given arguments */
 35318  	zTarget  = jx9_value_to_string(apArg[0], 0);
 35319  	zLink = jx9_value_to_string(apArg[1], 0);
 35320  	/* Perform the requested operation */
 35321  	rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
 35322  	/* IO result */
 35323  	jx9_result_bool(pCtx, rc == JX9_OK );
 35324  	return JX9_OK;
 35325  }
 35326  /*
 35327   * bool symlink(string $target, string $link)
 35328   *  Creates a symbolic link.
 35329   * Parameters
 35330   *  $target
 35331   *   Target of the link.
 35332   *  $link
 35333   *   The link name.
 35334   * Return
 35335   *  TRUE on success or FALSE on failure.
 35336   */
 35337  static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35338  {
 35339  	const char *zTarget, *zLink;
 35340  	jx9_vfs *pVfs;
 35341  	int rc;
 35342  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 35343  		/* Missing/Invalid arguments, return FALSE */
 35344  		jx9_result_bool(pCtx, 0);
 35345  		return JX9_OK;
 35346  	}
 35347  	/* Point to the underlying vfs */
 35348  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35349  	if( pVfs == 0 || pVfs->xLink == 0 ){
 35350  		/* IO routine not implemented, return NULL */
 35351  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35352  			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", 
 35353  			jx9_function_name(pCtx)
 35354  			);
 35355  		jx9_result_bool(pCtx, 0);
 35356  		return JX9_OK;
 35357  	}
 35358  	/* Extract the given arguments */
 35359  	zTarget  = jx9_value_to_string(apArg[0], 0);
 35360  	zLink = jx9_value_to_string(apArg[1], 0);
 35361  	/* Perform the requested operation */
 35362  	rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
 35363  	/* IO result */
 35364  	jx9_result_bool(pCtx, rc == JX9_OK );
 35365  	return JX9_OK;
 35366  }
 35367  /*
 35368   * int umask([ int $mask ])
 35369   *  Changes the current umask.
 35370   * Parameters
 35371   *  $mask
 35372   *   The new umask.
 35373   * Return
 35374   *  umask() without arguments simply returns the current umask.
 35375   *  Otherwise the old umask is returned.
 35376   */
 35377  static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35378  {
 35379  	int iOld, iNew;
 35380  	jx9_vfs *pVfs;
 35381  	/* Point to the underlying vfs */
 35382  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35383  	if( pVfs == 0 || pVfs->xUmask == 0 ){
 35384  		/* IO routine not implemented, return -1 */
 35385  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35386  			"IO routine(%s) not implemented in the underlying VFS", 
 35387  			jx9_function_name(pCtx)
 35388  			);
 35389  		jx9_result_int(pCtx, 0);
 35390  		return JX9_OK;
 35391  	}
 35392  	iNew = 0;
 35393  	if( nArg > 0 ){
 35394  		iNew = jx9_value_to_int(apArg[0]);
 35395  	}
 35396  	/* Perform the requested operation */
 35397  	iOld = pVfs->xUmask(iNew);
 35398  	/* Old mask */
 35399  	jx9_result_int(pCtx, iOld);
 35400  	return JX9_OK;
 35401  }
 35402  /*
 35403   * string sys_get_temp_dir()
 35404   *  Returns directory path used for temporary files.
 35405   * Parameters
 35406   *  None
 35407   * Return
 35408   *  Returns the path of the temporary directory.
 35409   */
 35410  static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35411  {
 35412  	jx9_vfs *pVfs;
 35413  	/* Set the empty string as the default return value */
 35414  	jx9_result_string(pCtx, "", 0);
 35415  	/* Point to the underlying vfs */
 35416  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35417  	if( pVfs == 0 || pVfs->xTempDir == 0 ){
 35418  		SXUNUSED(nArg); /* cc warning */
 35419  		SXUNUSED(apArg);
 35420  		/* IO routine not implemented, return "" */
 35421  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35422  			"IO routine(%s) not implemented in the underlying VFS", 
 35423  			jx9_function_name(pCtx)
 35424  			);		
 35425  		return JX9_OK;
 35426  	}
 35427  	/* Perform the requested operation */
 35428  	pVfs->xTempDir(pCtx);
 35429  	return JX9_OK;
 35430  }
 35431  /*
 35432   * string get_current_user()
 35433   *  Returns the name of the current working user.
 35434   * Parameters
 35435   *  None
 35436   * Return
 35437   *  Returns the name of the current working user.
 35438   */
 35439  static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35440  {
 35441  	jx9_vfs *pVfs;
 35442  	/* Point to the underlying vfs */
 35443  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35444  	if( pVfs == 0 || pVfs->xUsername == 0 ){
 35445  		SXUNUSED(nArg); /* cc warning */
 35446  		SXUNUSED(apArg);
 35447  		/* IO routine not implemented */
 35448  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35449  			"IO routine(%s) not implemented in the underlying VFS", 
 35450  			jx9_function_name(pCtx)
 35451  			);		
 35452  		/* Set a dummy username */
 35453  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 35454  		return JX9_OK;
 35455  	}
 35456  	/* Perform the requested operation */
 35457  	pVfs->xUsername(pCtx);
 35458  	return JX9_OK;
 35459  }
 35460  /*
 35461   * int64 getmypid()
 35462   *  Gets process ID.
 35463   * Parameters
 35464   *  None
 35465   * Return
 35466   *  Returns the process ID.
 35467   */
 35468  static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35469  {
 35470  	jx9_int64 nProcessId;
 35471  	jx9_vfs *pVfs;
 35472  	/* Point to the underlying vfs */
 35473  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35474  	if( pVfs == 0 || pVfs->xProcessId == 0 ){
 35475  		SXUNUSED(nArg); /* cc warning */
 35476  		SXUNUSED(apArg);
 35477  		/* IO routine not implemented, return -1 */
 35478  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35479  			"IO routine(%s) not implemented in the underlying VFS", 
 35480  			jx9_function_name(pCtx)
 35481  			);
 35482  		jx9_result_int(pCtx, -1);
 35483  		return JX9_OK;
 35484  	}
 35485  	/* Perform the requested operation */
 35486  	nProcessId = (jx9_int64)pVfs->xProcessId();
 35487  	/* Set the result */
 35488  	jx9_result_int64(pCtx, nProcessId);
 35489  	return JX9_OK;
 35490  }
 35491  /*
 35492   * int getmyuid()
 35493   *  Get user ID.
 35494   * Parameters
 35495   *  None
 35496   * Return
 35497   *  Returns the user ID.
 35498   */
 35499  static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35500  {
 35501  	jx9_vfs *pVfs;
 35502  	int nUid;
 35503  	/* Point to the underlying vfs */
 35504  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35505  	if( pVfs == 0 || pVfs->xUid == 0 ){
 35506  		SXUNUSED(nArg); /* cc warning */
 35507  		SXUNUSED(apArg);
 35508  		/* IO routine not implemented, return -1 */
 35509  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35510  			"IO routine(%s) not implemented in the underlying VFS", 
 35511  			jx9_function_name(pCtx)
 35512  			);
 35513  		jx9_result_int(pCtx, -1);
 35514  		return JX9_OK;
 35515  	}
 35516  	/* Perform the requested operation */
 35517  	nUid = pVfs->xUid();
 35518  	/* Set the result */
 35519  	jx9_result_int(pCtx, nUid);
 35520  	return JX9_OK;
 35521  }
 35522  /*
 35523   * int getmygid()
 35524   *  Get group ID.
 35525   * Parameters
 35526   *  None
 35527   * Return
 35528   *  Returns the group ID.
 35529   */
 35530  static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35531  {
 35532  	jx9_vfs *pVfs;
 35533  	int nGid;
 35534  	/* Point to the underlying vfs */
 35535  	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
 35536  	if( pVfs == 0 || pVfs->xGid == 0 ){
 35537  		SXUNUSED(nArg); /* cc warning */
 35538  		SXUNUSED(apArg);
 35539  		/* IO routine not implemented, return -1 */
 35540  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35541  			"IO routine(%s) not implemented in the underlying VFS", 
 35542  			jx9_function_name(pCtx)
 35543  			);
 35544  		jx9_result_int(pCtx, -1);
 35545  		return JX9_OK;
 35546  	}
 35547  	/* Perform the requested operation */
 35548  	nGid = pVfs->xGid();
 35549  	/* Set the result */
 35550  	jx9_result_int(pCtx, nGid);
 35551  	return JX9_OK;
 35552  }
 35553  #ifdef __WINNT__
 35554  #include <Windows.h>
 35555  #elif defined(__UNIXES__)
 35556  #include <sys/utsname.h>
 35557  #endif
 35558  /*
 35559   * string uname([ string $mode = "a" ])
 35560   *  Returns information about the host operating system.
 35561   * Parameters
 35562   *  $mode
 35563   *   mode is a single character that defines what information is returned:
 35564   *    'a': This is the default. Contains all modes in the sequence "s n r v m".
 35565   *    's': Operating system name. eg. FreeBSD.
 35566   *    'n': Host name. eg. localhost.example.com.
 35567   *    'r': Release name. eg. 5.1.2-RELEASE.
 35568   *    'v': Version information. Varies a lot between operating systems.
 35569   *    'm': Machine type. eg. i386.
 35570   * Return
 35571   *  OS description as a string.
 35572   */
 35573  static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35574  {
 35575  #if defined(__WINNT__)
 35576  	const char *zName = "Microsoft Windows";
 35577  	OSVERSIONINFOW sVer;
 35578  #elif defined(__UNIXES__)
 35579  	struct utsname sName;
 35580  #endif
 35581  	const char *zMode = "a";
 35582  	if( nArg > 0 && jx9_value_is_string(apArg[0]) ){
 35583  		/* Extract the desired mode */
 35584  		zMode = jx9_value_to_string(apArg[0], 0);
 35585  	}
 35586  #if defined(__WINNT__)
 35587  	sVer.dwOSVersionInfoSize = sizeof(sVer);
 35588  	if( TRUE != GetVersionExW(&sVer)){
 35589  		jx9_result_string(pCtx, zName, -1);
 35590  		return JX9_OK;
 35591  	}
 35592  	if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){
 35593  		if( sVer.dwMajorVersion <= 4 ){
 35594  			zName = "Microsoft Windows NT";
 35595  		}else if( sVer.dwMajorVersion == 5 ){
 35596  			switch(sVer.dwMinorVersion){
 35597  				case 0:	zName = "Microsoft Windows 2000"; break;
 35598  				case 1: zName = "Microsoft Windows XP";   break;
 35599  				case 2: zName = "Microsoft Windows Server 2003"; break;
 35600  			}
 35601  		}else if( sVer.dwMajorVersion == 6){
 35602  				switch(sVer.dwMinorVersion){
 35603  					case 0: zName = "Microsoft Windows Vista"; break;
 35604  					case 1: zName = "Microsoft Windows 7"; break;
 35605  					case 2: zName = "Microsoft Windows 8"; break;
 35606  					default: break;
 35607  				}
 35608  		}
 35609  	}
 35610  	switch(zMode[0]){
 35611  	case 's':
 35612  		/* Operating system name */
 35613  		jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
 35614  		break;
 35615  	case 'n':
 35616  		/* Host name */
 35617  		jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
 35618  		break;
 35619  	case 'r':
 35620  	case 'v':
 35621  		/* Version information. */
 35622  		jx9_result_string_format(pCtx, "%u.%u build %u", 
 35623  			sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
 35624  			);
 35625  		break;
 35626  	case 'm':
 35627  		/* Machine name */
 35628  		jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
 35629  		break;
 35630  	default:
 35631  		jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86", 
 35632  			zName, 
 35633  			sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
 35634  			);
 35635  		break;
 35636  	}
 35637  #elif defined(__UNIXES__)
 35638  	if( uname(&sName) != 0 ){
 35639  		jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
 35640  		return JX9_OK;
 35641  	}
 35642  	switch(zMode[0]){
 35643  	case 's':
 35644  		/* Operating system name */
 35645  		jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
 35646  		break;
 35647  	case 'n':
 35648  		/* Host name */
 35649  		jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
 35650  		break;
 35651  	case 'r':
 35652  		/* Release information */
 35653  		jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
 35654  		break;
 35655  	case 'v':
 35656  		/* Version information. */
 35657  		jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
 35658  		break;
 35659  	case 'm':
 35660  		/* Machine name */
 35661  		jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
 35662  		break;
 35663  	default:
 35664  		jx9_result_string_format(pCtx, 
 35665  			"%s %s %s %s %s", 
 35666  			sName.sysname, 
 35667  			sName.release, 
 35668  			sName.version, 
 35669  			sName.nodename, 
 35670  			sName.machine
 35671  			);
 35672  		break;
 35673  	}
 35674  #else
 35675  	jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
 35676  #endif
 35677  	return JX9_OK;
 35678  }
 35679  /*
 35680   * Section:
 35681   *    IO stream implementation.
 35682   * Authors:
 35683   *    Symisc Systems, devel@symisc.net.
 35684   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 35685   * Status:
 35686   *    Stable.
 35687   */
 35688  typedef struct io_private io_private;
 35689  struct io_private
 35690  {
 35691  	const jx9_io_stream *pStream; /* Underlying IO device */
 35692  	void *pHandle; /* IO handle */
 35693  	/* Unbuffered IO */
 35694  	SyBlob sBuffer; /* Working buffer */
 35695  	sxu32 nOfft;    /* Current read offset */
 35696  	sxu32 iMagic;   /* Sanity check to avoid misuse */
 35697  };
 35698  #define IO_PRIVATE_MAGIC 0xFEAC14
 35699  /* Make sure we are dealing with a valid io_private instance */
 35700  #define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC ) 
 35701  /* Forward declaration */
 35702  static void ResetIOPrivate(io_private *pDev);
 35703  /*
 35704   * bool ftruncate(resource $handle, int64 $size)
 35705   *  Truncates a file to a given length.
 35706   * Parameters
 35707   *  $handle
 35708   *   The file pointer.
 35709   *   Note:
 35710   *    The handle must be open for writing.
 35711   * $size
 35712   *   The size to truncate to.
 35713   * Return
 35714   *  TRUE on success or FALSE on failure.
 35715   */
 35716  static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35717  {
 35718  	const jx9_io_stream *pStream;
 35719  	io_private *pDev;
 35720  	int rc;
 35721  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
 35722  		/* Missing/Invalid arguments, return FALSE */
 35723  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35724  		jx9_result_bool(pCtx, 0);
 35725  		return JX9_OK;
 35726  	}
 35727  	/* Extract our private data */
 35728  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35729  	/* Make sure we are dealing with a valid io_private instance */
 35730  	if( IO_PRIVATE_INVALID(pDev) ){
 35731  		/*Expecting an IO handle */
 35732  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35733  		jx9_result_bool(pCtx, 0);
 35734  		return JX9_OK;
 35735  	}
 35736  	/* Point to the target IO stream device */
 35737  	pStream = pDev->pStream;
 35738  	if( pStream == 0  || pStream->xTrunc == 0){
 35739  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35740  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 35741  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35742  			);
 35743  		jx9_result_bool(pCtx, 0);
 35744  		return JX9_OK;
 35745  	}
 35746  	/* Perform the requested operation */
 35747  	rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
 35748  	if( rc == JX9_OK ){
 35749  		/* Discard buffered data */
 35750  		ResetIOPrivate(pDev);
 35751  	}
 35752  	/* IO result */
 35753  	jx9_result_bool(pCtx, rc == JX9_OK);
 35754  	return JX9_OK;	
 35755  }
 35756  /*
 35757   * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ])
 35758   *  Seeks on a file pointer.
 35759   * Parameters
 35760   *  $handle
 35761   *   A file system pointer resource that is typically created using fopen().
 35762   * $offset
 35763   *   The offset.
 35764   *   To move to a position before the end-of-file, you need to pass a negative
 35765   *   value in offset and set whence to SEEK_END.
 35766   *   whence
 35767   *   whence values are:
 35768   *    SEEK_SET - Set position equal to offset bytes.
 35769   *    SEEK_CUR - Set position to current location plus offset.
 35770   *    SEEK_END - Set position to end-of-file plus offset.
 35771   * Return
 35772   *  0 on success, -1 on failure
 35773   */
 35774  static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35775  {
 35776  	const jx9_io_stream *pStream;
 35777  	io_private *pDev;
 35778  	jx9_int64 iOfft;
 35779  	int whence;  
 35780  	int rc;
 35781  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
 35782  		/* Missing/Invalid arguments, return FALSE */
 35783  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35784  		jx9_result_int(pCtx, -1);
 35785  		return JX9_OK;
 35786  	}
 35787  	/* Extract our private data */
 35788  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35789  	/* Make sure we are dealing with a valid io_private instance */
 35790  	if( IO_PRIVATE_INVALID(pDev) ){
 35791  		/*Expecting an IO handle */
 35792  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35793  		jx9_result_int(pCtx, -1);
 35794  		return JX9_OK;
 35795  	}
 35796  	/* Point to the target IO stream device */
 35797  	pStream = pDev->pStream;
 35798  	if( pStream == 0  || pStream->xSeek == 0){
 35799  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35800  			"IO routine(%s) not implemented in the underlying stream(%s) device", 
 35801  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35802  			);
 35803  		jx9_result_int(pCtx, -1);
 35804  		return JX9_OK;
 35805  	}
 35806  	/* Extract the offset */
 35807  	iOfft = jx9_value_to_int64(apArg[1]);
 35808  	whence = 0;/* SEEK_SET */
 35809  	if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
 35810  		whence = jx9_value_to_int(apArg[2]);
 35811  	}
 35812  	/* Perform the requested operation */
 35813  	rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
 35814  	if( rc == JX9_OK ){
 35815  		/* Ignore buffered data */
 35816  		ResetIOPrivate(pDev);
 35817  	}
 35818  	/* IO result */
 35819  	jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
 35820  	return JX9_OK;	
 35821  }
 35822  /*
 35823   * int64 ftell(resource $handle)
 35824   *  Returns the current position of the file read/write pointer.
 35825   * Parameters
 35826   *  $handle
 35827   *   The file pointer.
 35828   * Return
 35829   *  Returns the position of the file pointer referenced by handle
 35830   *  as an integer; i.e., its offset into the file stream.
 35831   *  FALSE is returned on failure.
 35832   */
 35833  static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35834  {
 35835  	const jx9_io_stream *pStream;
 35836  	io_private *pDev;
 35837  	jx9_int64 iOfft;
 35838  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 35839  		/* Missing/Invalid arguments, return FALSE */
 35840  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35841  		jx9_result_bool(pCtx, 0);
 35842  		return JX9_OK;
 35843  	}
 35844  	/* Extract our private data */
 35845  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35846  	/* Make sure we are dealing with a valid io_private instance */
 35847  	if( IO_PRIVATE_INVALID(pDev) ){
 35848  		/*Expecting an IO handle */
 35849  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35850  		jx9_result_bool(pCtx, 0);
 35851  		return JX9_OK;
 35852  	}
 35853  	/* Point to the target IO stream device */
 35854  	pStream = pDev->pStream;
 35855  	if( pStream == 0  || pStream->xTell == 0){
 35856  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35857  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 35858  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35859  			);
 35860  		jx9_result_bool(pCtx, 0);
 35861  		return JX9_OK;
 35862  	}
 35863  	/* Perform the requested operation */
 35864  	iOfft = pStream->xTell(pDev->pHandle);
 35865  	/* IO result */
 35866  	jx9_result_int64(pCtx, iOfft);
 35867  	return JX9_OK;	
 35868  }
 35869  /*
 35870   * bool rewind(resource $handle)
 35871   *  Rewind the position of a file pointer.
 35872   * Parameters
 35873   *  $handle
 35874   *   The file pointer.
 35875   * Return
 35876   *  TRUE on success or FALSE on failure.
 35877   */
 35878  static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35879  {
 35880  	const jx9_io_stream *pStream;
 35881  	io_private *pDev;
 35882  	int rc;
 35883  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 35884  		/* Missing/Invalid arguments, return FALSE */
 35885  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35886  		jx9_result_bool(pCtx, 0);
 35887  		return JX9_OK;
 35888  	}
 35889  	/* Extract our private data */
 35890  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35891  	/* Make sure we are dealing with a valid io_private instance */
 35892  	if( IO_PRIVATE_INVALID(pDev) ){
 35893  		/*Expecting an IO handle */
 35894  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35895  		jx9_result_bool(pCtx, 0);
 35896  		return JX9_OK;
 35897  	}
 35898  	/* Point to the target IO stream device */
 35899  	pStream = pDev->pStream;
 35900  	if( pStream == 0  || pStream->xSeek == 0){
 35901  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35902  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 35903  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35904  			);
 35905  		jx9_result_bool(pCtx, 0);
 35906  		return JX9_OK;
 35907  	}
 35908  	/* Perform the requested operation */
 35909  	rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
 35910  	if( rc == JX9_OK ){
 35911  		/* Ignore buffered data */
 35912  		ResetIOPrivate(pDev);
 35913  	}
 35914  	/* IO result */
 35915  	jx9_result_bool(pCtx, rc == JX9_OK);
 35916  	return JX9_OK;	
 35917  }
 35918  /*
 35919   * bool fflush(resource $handle)
 35920   *  Flushes the output to a file.
 35921   * Parameters
 35922   *  $handle
 35923   *   The file pointer.
 35924   * Return
 35925   *  TRUE on success or FALSE on failure.
 35926   */
 35927  static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35928  {
 35929  	const jx9_io_stream *pStream;
 35930  	io_private *pDev;
 35931  	int rc;
 35932  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 35933  		/* Missing/Invalid arguments, return FALSE */
 35934  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35935  		jx9_result_bool(pCtx, 0);
 35936  		return JX9_OK;
 35937  	}
 35938  	/* Extract our private data */
 35939  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35940  	/* Make sure we are dealing with a valid io_private instance */
 35941  	if( IO_PRIVATE_INVALID(pDev) ){
 35942  		/*Expecting an IO handle */
 35943  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35944  		jx9_result_bool(pCtx, 0);
 35945  		return JX9_OK;
 35946  	}
 35947  	/* Point to the target IO stream device */
 35948  	pStream = pDev->pStream;
 35949  	if( pStream == 0 || pStream->xSync == 0){
 35950  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35951  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 35952  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35953  			);
 35954  		jx9_result_bool(pCtx, 0);
 35955  		return JX9_OK;
 35956  	}
 35957  	/* Perform the requested operation */
 35958  	rc = pStream->xSync(pDev->pHandle);
 35959  	/* IO result */
 35960  	jx9_result_bool(pCtx, rc == JX9_OK);
 35961  	return JX9_OK;	
 35962  }
 35963  /*
 35964   * bool feof(resource $handle)
 35965   *  Tests for end-of-file on a file pointer.
 35966   * Parameters
 35967   *  $handle
 35968   *   The file pointer.
 35969   * Return
 35970   *  Returns TRUE if the file pointer is at EOF.FALSE otherwise
 35971   */
 35972  static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
 35973  {
 35974  	const jx9_io_stream *pStream;
 35975  	io_private *pDev;
 35976  	int rc;
 35977  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 35978  		/* Missing/Invalid arguments */
 35979  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35980  		jx9_result_bool(pCtx, 1);
 35981  		return JX9_OK;
 35982  	}
 35983  	/* Extract our private data */
 35984  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 35985  	/* Make sure we are dealing with a valid io_private instance */
 35986  	if( IO_PRIVATE_INVALID(pDev) ){
 35987  		/*Expecting an IO handle */
 35988  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 35989  		jx9_result_bool(pCtx, 1);
 35990  		return JX9_OK;
 35991  	}
 35992  	/* Point to the target IO stream device */
 35993  	pStream = pDev->pStream;
 35994  	if( pStream == 0 ){
 35995  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 35996  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 35997  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 35998  			);
 35999  		jx9_result_bool(pCtx, 1);
 36000  		return JX9_OK;
 36001  	}
 36002  	rc = SXERR_EOF;
 36003  	/* Perform the requested operation */
 36004  	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
 36005  		/* Data is available */
 36006  		rc = JX9_OK;
 36007  	}else{
 36008  		char zBuf[4096];
 36009  		jx9_int64 n;
 36010  		/* Perform a buffered read */
 36011  		n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf));
 36012  		if( n > 0 ){
 36013  			/* Copy buffered data */
 36014  			SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
 36015  			rc = JX9_OK;
 36016  		}
 36017  	}
 36018  	/* EOF or not */
 36019  	jx9_result_bool(pCtx, rc == SXERR_EOF);
 36020  	return JX9_OK;	
 36021  }
 36022  /*
 36023   * Read n bytes from the underlying IO stream device.
 36024   * Return total numbers of bytes readen on success. A number < 1 on failure 
 36025   * [i.e: IO error ] or EOF.
 36026   */
 36027  static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
 36028  {
 36029  	const jx9_io_stream *pStream = pDev->pStream;
 36030  	char *zBuf = (char *)pBuf;
 36031  	jx9_int64 n, nRead;
 36032  	n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
 36033  	if( n > 0 ){
 36034  		if( n > nLen ){
 36035  			n = nLen;
 36036  		}
 36037  		/* Copy the buffered data */
 36038  		SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n);
 36039  		/* Update the read offset */
 36040  		pDev->nOfft += (sxu32)n;
 36041  		if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
 36042  			/* Reset the working buffer so that we avoid excessive memory allocation */
 36043  			SyBlobReset(&pDev->sBuffer);
 36044  			pDev->nOfft = 0;
 36045  		}
 36046  		nLen -= n;
 36047  		if( nLen < 1 ){
 36048  			/* All done */
 36049  			return n;
 36050  		}
 36051  		/* Advance the cursor */
 36052  		zBuf += n;
 36053  	}
 36054  	/* Read without buffering */
 36055  	nRead = pStream->xRead(pDev->pHandle, zBuf, nLen);
 36056  	if( nRead > 0 ){
 36057  		n += nRead;
 36058  	}else if( n < 1 ){
 36059  		/* EOF or IO error */
 36060  		return nRead;
 36061  	}
 36062  	return n;
 36063  }
 36064  /*
 36065   * Extract a single line from the buffered input.
 36066   */
 36067  static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine)
 36068  {
 36069  	const char *zIn, *zEnd, *zPtr;
 36070  	zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
 36071  	zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft];
 36072  	zPtr = zIn;
 36073  	while( zIn < zEnd ){
 36074  		if( zIn[0] == '\n' ){
 36075  			/* Line found */
 36076  			zIn++; /* Include the line ending as requested by the JX9 specification */
 36077  			*pLen = (jx9_int64)(zIn-zPtr);
 36078  			*pzLine = zPtr;
 36079  			return SXRET_OK;
 36080  		}
 36081  		zIn++;
 36082  	}
 36083  	/* No line were found */
 36084  	return SXERR_NOTFOUND;
 36085  }
 36086  /*
 36087   * Read a single line from the underlying IO stream device.
 36088   */
 36089  static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
 36090  {
 36091  	const jx9_io_stream *pStream = pDev->pStream;
 36092  	char zBuf[8192];
 36093  	jx9_int64 n;
 36094  	sxi32 rc;
 36095  	n = 0;
 36096  	if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
 36097  		/* Reset the working buffer so that we avoid excessive memory allocation */
 36098  		SyBlobReset(&pDev->sBuffer);
 36099  		pDev->nOfft = 0;
 36100  	}
 36101  	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
 36102  		/* Check if there is a line */
 36103  		rc = GetLine(pDev, &n, pzData);
 36104  		if( rc == SXRET_OK ){
 36105  			/* Got line, update the cursor  */
 36106  			pDev->nOfft += (sxu32)n;
 36107  			return n;
 36108  		}
 36109  	}
 36110  	/* Perform the read operation until a new line is extracted or length
 36111  	 * limit is reached.
 36112  	 */
 36113  	for(;;){
 36114  		n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf));
 36115  		if( n < 1 ){
 36116  			/* EOF or IO error */
 36117  			break;
 36118  		}
 36119  		/* Append the data just read */
 36120  		SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
 36121  		/* Try to extract a line */
 36122  		rc = GetLine(pDev, &n, pzData);
 36123  		if( rc == SXRET_OK ){
 36124  			/* Got one, return immediately */
 36125  			pDev->nOfft += (sxu32)n;
 36126  			return n;
 36127  		}
 36128  		if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){
 36129  			/* Read limit reached, return the available data */
 36130  			*pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
 36131  			n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
 36132  			/* Reset the working buffer */
 36133  			SyBlobReset(&pDev->sBuffer);
 36134  			pDev->nOfft = 0;
 36135  			return n;
 36136  		}
 36137  	}
 36138  	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
 36139  		/* Read limit reached, return the available data */
 36140  		*pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
 36141  		n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
 36142  		/* Reset the working buffer */
 36143  		SyBlobReset(&pDev->sBuffer);
 36144  		pDev->nOfft = 0;
 36145  	}
 36146  	return n;
 36147  }
 36148  /*
 36149   * Open an IO stream handle.
 36150   * Notes on stream:
 36151   * According to the JX9 reference manual.
 36152   * In its simplest definition, a stream is a resource object which exhibits streamable behavior.
 36153   * That is, it can be read from or written to in a linear fashion, and may be able to fseek() 
 36154   * to an arbitrary locations within the stream.
 36155   * A wrapper is additional code which tells the stream how to handle specific protocols/encodings.
 36156   * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file
 36157   * on a remote server.
 36158   * A stream is referenced as: scheme://target
 36159   *   scheme(string) - The name of the wrapper to be used. Examples include: file, http...
 36160   *   If no wrapper is specified, the function default is used (typically file://).
 36161   *   target - Depends on the wrapper used. For filesystem related streams this is typically a path
 36162   *  and filename of the desired file. For network related streams this is typically a hostname, often
 36163   *  with a path appended. 
 36164   *
 36165   * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately.
 36166   * Please refer to the official documentation for a full discussion.
 36167   * This function return a handle on success. Otherwise null.
 36168   */
 36169  JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile, 
 36170  	int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew)
 36171  {
 36172  	void *pHandle = 0; /* cc warning */
 36173  	SyString sFile;
 36174  	int rc;
 36175  	if( pStream == 0 ){
 36176  		/* No such stream device */
 36177  		return 0;
 36178  	}
 36179  	SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
 36180  	if( use_include ){
 36181  		if(	sFile.zString[0] == '/' ||
 36182  #ifdef __WINNT__
 36183  			(sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) ||
 36184  #endif
 36185  			(sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') ||
 36186  			(sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){
 36187  				/*  Open the file directly */
 36188  				rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
 36189  		}else{
 36190  			SyString *pPath;
 36191  			SyBlob sWorker;
 36192  #ifdef __WINNT__
 36193  			static const int c = '\\';
 36194  #else
 36195  			static const int c = '/';
 36196  #endif
 36197  			/* Init the path builder working buffer */
 36198  			SyBlobInit(&sWorker, &pVm->sAllocator);
 36199  			/* Build a path from the set of include path */
 36200  			SySetResetCursor(&pVm->aPaths);
 36201  			rc = SXERR_IO;
 36202  			while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){
 36203  				/* Build full path */
 36204  				SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile);
 36205  				/* Append null terminator */
 36206  				if( SXRET_OK != SyBlobNullAppend(&sWorker) ){
 36207  					continue;
 36208  				}
 36209  				/* Try to open the file */
 36210  				rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle);
 36211  				if( rc == JX9_OK ){
 36212  					if( bPushInclude ){
 36213  						/* Mark as included */
 36214  						jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
 36215  					}
 36216  					break;
 36217  				}
 36218  				/* Reset the working buffer */
 36219  				SyBlobReset(&sWorker);
 36220  				/* Check the next path */
 36221  			}
 36222  			SyBlobRelease(&sWorker);
 36223  		}
 36224  		if( rc == JX9_OK ){
 36225  			if( bPushInclude ){
 36226  				/* Mark as included */
 36227  				jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
 36228  			}
 36229  		}
 36230  	}else{
 36231  		/* Open the URI direcly */
 36232  		rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
 36233  	}
 36234  	if( rc != JX9_OK ){
 36235  		/* IO error */
 36236  		return 0;
 36237  	}
 36238  	/* Return the file handle */
 36239  	return pHandle;
 36240  }
 36241  /*
 36242   * Read the whole contents of an open IO stream handle [i.e local file/URL..]
 36243   * Store the read data in the given BLOB (last argument).
 36244   * The read operation is stopped when he hit the EOF or an IO error occurs.
 36245   */
 36246  JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
 36247  {
 36248  	jx9_int64 nRead;
 36249  	char zBuf[8192]; /* 8K */
 36250  	int rc;
 36251  	/* Perform the requested operation */
 36252  	for(;;){
 36253  		nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
 36254  		if( nRead < 1 ){
 36255  			/* EOF or IO error */
 36256  			break;
 36257  		}
 36258  		/* Append contents */
 36259  		rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead);
 36260  		if( rc != SXRET_OK ){
 36261  			break;
 36262  		}
 36263  	}
 36264  	return SyBlobLength(pOut) > 0 ? SXRET_OK : -1;
 36265  }
 36266  /*
 36267   * Close an open IO stream handle [i.e local file/URI..].
 36268   */
 36269  JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle)
 36270  {
 36271  	if( pStream->xClose ){
 36272  		pStream->xClose(pHandle);
 36273  	}
 36274  }
 36275  /*
 36276   * string fgetc(resource $handle)
 36277   *  Gets a character from the given file pointer.
 36278   * Parameters
 36279   *  $handle
 36280   *   The file pointer.
 36281   * Return
 36282   *  Returns a string containing a single character read from the file
 36283   *  pointed to by handle. Returns FALSE on EOF. 
 36284   * WARNING
 36285   *  This operation is extremely slow.Avoid using it.
 36286   */
 36287  static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36288  {
 36289  	const jx9_io_stream *pStream;
 36290  	io_private *pDev;
 36291  	int c, n;
 36292  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36293  		/* Missing/Invalid arguments, return FALSE */
 36294  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36295  		jx9_result_bool(pCtx, 0);
 36296  		return JX9_OK;
 36297  	}
 36298  	/* Extract our private data */
 36299  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36300  	/* Make sure we are dealing with a valid io_private instance */
 36301  	if( IO_PRIVATE_INVALID(pDev) ){
 36302  		/*Expecting an IO handle */
 36303  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36304  		jx9_result_bool(pCtx, 0);
 36305  		return JX9_OK;
 36306  	}
 36307  	/* Point to the target IO stream device */
 36308  	pStream = pDev->pStream;
 36309  	if( pStream == 0  ){
 36310  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36311  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36312  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36313  			);
 36314  		jx9_result_bool(pCtx, 0);
 36315  		return JX9_OK;
 36316  	}
 36317  	/* Perform the requested operation */
 36318  	n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
 36319  	/* IO result */
 36320  	if( n < 1 ){
 36321  		/* EOF or error, return FALSE */
 36322  		jx9_result_bool(pCtx, 0);
 36323  	}else{
 36324  		/* Return the string holding the character */
 36325  		jx9_result_string(pCtx, (const char *)&c, sizeof(char));
 36326  	}
 36327  	return JX9_OK;	
 36328  }
 36329  /*
 36330   * string fgets(resource $handle[, int64 $length ])
 36331   *  Gets line from file pointer.
 36332   * Parameters
 36333   *  $handle
 36334   *   The file pointer.
 36335   * $length
 36336   *  Reading ends when length - 1 bytes have been read, on a newline
 36337   *  (which is included in the return value), or on EOF (whichever comes first).
 36338   *  If no length is specified, it will keep reading from the stream until it reaches
 36339   *  the end of the line. 
 36340   * Return
 36341   *  Returns a string of up to length - 1 bytes read from the file pointed to by handle.
 36342   *  If there is no more data to read in the file pointer, then FALSE is returned.
 36343   *  If an error occurs, FALSE is returned.
 36344   */
 36345  static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36346  {
 36347  	const jx9_io_stream *pStream;
 36348  	const char *zLine;
 36349  	io_private *pDev;
 36350  	jx9_int64 n, nLen;
 36351  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36352  		/* Missing/Invalid arguments, return FALSE */
 36353  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36354  		jx9_result_bool(pCtx, 0);
 36355  		return JX9_OK;
 36356  	}
 36357  	/* Extract our private data */
 36358  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36359  	/* Make sure we are dealing with a valid io_private instance */
 36360  	if( IO_PRIVATE_INVALID(pDev) ){
 36361  		/*Expecting an IO handle */
 36362  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36363  		jx9_result_bool(pCtx, 0);
 36364  		return JX9_OK;
 36365  	}
 36366  	/* Point to the target IO stream device */
 36367  	pStream = pDev->pStream;
 36368  	if( pStream == 0  ){
 36369  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36370  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36371  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36372  			);
 36373  		jx9_result_bool(pCtx, 0);
 36374  		return JX9_OK;
 36375  	}
 36376  	nLen = -1;
 36377  	if( nArg > 1 ){
 36378  		/* Maximum data to read */
 36379  		nLen = jx9_value_to_int64(apArg[1]);
 36380  	}
 36381  	/* Perform the requested operation */
 36382  	n = StreamReadLine(pDev, &zLine, nLen);
 36383  	if( n < 1 ){
 36384  		/* EOF or IO error, return FALSE */
 36385  		jx9_result_bool(pCtx, 0);
 36386  	}else{
 36387  		/* Return the freshly extracted line */
 36388  		jx9_result_string(pCtx, zLine, (int)n);
 36389  	}
 36390  	return JX9_OK;	
 36391  }
 36392  /*
 36393   * string fread(resource $handle, int64 $length)
 36394   *  Binary-safe file read.
 36395   * Parameters
 36396   *  $handle
 36397   *   The file pointer.
 36398   * $length
 36399   *  Up to length number of bytes read.
 36400   * Return
 36401   *  The data readen on success or FALSE on failure. 
 36402   */
 36403  static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36404  {
 36405  	const jx9_io_stream *pStream;
 36406  	io_private *pDev;
 36407  	jx9_int64 nRead;
 36408  	void *pBuf;
 36409  	int nLen;
 36410  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36411  		/* Missing/Invalid arguments, return FALSE */
 36412  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36413  		jx9_result_bool(pCtx, 0);
 36414  		return JX9_OK;
 36415  	}
 36416  	/* Extract our private data */
 36417  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36418  	/* Make sure we are dealing with a valid io_private instance */
 36419  	if( IO_PRIVATE_INVALID(pDev) ){
 36420  		/*Expecting an IO handle */
 36421  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36422  		jx9_result_bool(pCtx, 0);
 36423  		return JX9_OK;
 36424  	}
 36425  	/* Point to the target IO stream device */
 36426  	pStream = pDev->pStream;
 36427  	if( pStream == 0  ){
 36428  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36429  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36430  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36431  			);
 36432  		jx9_result_bool(pCtx, 0);
 36433  		return JX9_OK;
 36434  	}
 36435          nLen = 4096;
 36436  	if( nArg > 1 ){
 36437   	  nLen = jx9_value_to_int(apArg[1]);
 36438  	  if( nLen < 1 ){
 36439  		/* Invalid length, set a default length */
 36440  		nLen = 4096;
 36441  	  }
 36442          }
 36443  	/* Allocate enough buffer */
 36444  	pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
 36445  	if( pBuf == 0 ){
 36446  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");			
 36447  		jx9_result_bool(pCtx, 0);
 36448  		return JX9_OK;
 36449  	}
 36450  	/* Perform the requested operation */
 36451  	nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
 36452  	if( nRead < 1 ){
 36453  		/* Nothing read, return FALSE */
 36454  		jx9_result_bool(pCtx, 0);
 36455  	}else{
 36456  		/* Make a copy of the data just read */
 36457  		jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
 36458  	}
 36459  	/* Release the buffer */
 36460  	jx9_context_free_chunk(pCtx, pBuf);
 36461  	return JX9_OK;	
 36462  }
 36463  /*
 36464   * array fgetcsv(resource $handle [, int $length = 0 
 36465   *         [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]])
 36466   * Gets line from file pointer and parse for CSV fields.
 36467   * Parameters
 36468   * $handle
 36469   *   The file pointer.
 36470   * $length
 36471   *  Reading ends when length - 1 bytes have been read, on a newline
 36472   *  (which is included in the return value), or on EOF (whichever comes first).
 36473   *  If no length is specified, it will keep reading from the stream until it reaches
 36474   *  the end of the line. 
 36475   * $delimiter
 36476   *   Set the field delimiter (one character only).
 36477   * $enclosure
 36478   *   Set the field enclosure character (one character only).
 36479   * $escape
 36480   *   Set the escape character (one character only). Defaults as a backslash (\)
 36481   * Return
 36482   *  Returns a string of up to length - 1 bytes read from the file pointed to by handle.
 36483   *  If there is no more data to read in the file pointer, then FALSE is returned.
 36484   *  If an error occurs, FALSE is returned.
 36485   */
 36486  static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36487  {
 36488  	const jx9_io_stream *pStream;
 36489  	const char *zLine;
 36490  	io_private *pDev;
 36491  	jx9_int64 n, nLen;
 36492  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36493  		/* Missing/Invalid arguments, return FALSE */
 36494  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36495  		jx9_result_bool(pCtx, 0);
 36496  		return JX9_OK;
 36497  	}
 36498  	/* Extract our private data */
 36499  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36500  	/* Make sure we are dealing with a valid io_private instance */
 36501  	if( IO_PRIVATE_INVALID(pDev) ){
 36502  		/*Expecting an IO handle */
 36503  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36504  		jx9_result_bool(pCtx, 0);
 36505  		return JX9_OK;
 36506  	}
 36507  	/* Point to the target IO stream device */
 36508  	pStream = pDev->pStream;
 36509  	if( pStream == 0  ){
 36510  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36511  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36512  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36513  			);
 36514  		jx9_result_bool(pCtx, 0);
 36515  		return JX9_OK;
 36516  	}
 36517  	nLen = -1;
 36518  	if( nArg > 1 ){
 36519  		/* Maximum data to read */
 36520  		nLen = jx9_value_to_int64(apArg[1]);
 36521  	}
 36522  	/* Perform the requested operation */
 36523  	n = StreamReadLine(pDev, &zLine, nLen);
 36524  	if( n < 1 ){
 36525  		/* EOF or IO error, return FALSE */
 36526  		jx9_result_bool(pCtx, 0);
 36527  	}else{
 36528  		jx9_value *pArray;
 36529  		int delim  = ',';   /* Delimiter */
 36530  		int encl   = '"' ;  /* Enclosure */
 36531  		int escape = '\\';  /* Escape character */
 36532  		if( nArg > 2 ){
 36533  			const char *zPtr;
 36534  			int i;
 36535  			if( jx9_value_is_string(apArg[2]) ){
 36536  				/* Extract the delimiter */
 36537  				zPtr = jx9_value_to_string(apArg[2], &i);
 36538  				if( i > 0 ){
 36539  					delim = zPtr[0];
 36540  				}
 36541  			}
 36542  			if( nArg > 3 ){
 36543  				if( jx9_value_is_string(apArg[3]) ){
 36544  					/* Extract the enclosure */
 36545  					zPtr = jx9_value_to_string(apArg[3], &i);
 36546  					if( i > 0 ){
 36547  						encl = zPtr[0];
 36548  					}
 36549  				}
 36550  				if( nArg > 4 ){
 36551  					if( jx9_value_is_string(apArg[4]) ){
 36552  						/* Extract the escape character */
 36553  						zPtr = jx9_value_to_string(apArg[4], &i);
 36554  						if( i > 0 ){
 36555  							escape = zPtr[0];
 36556  						}
 36557  					}
 36558  				}
 36559  			}
 36560  		}
 36561  		/* Create our array */
 36562  		pArray = jx9_context_new_array(pCtx);
 36563  		if( pArray == 0 ){
 36564  			jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 36565  			jx9_result_null(pCtx);
 36566  			return JX9_OK;
 36567  		}
 36568  		/* Parse the raw input */
 36569  		jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
 36570  		/* Return the freshly created array  */
 36571  		jx9_result_value(pCtx, pArray);
 36572  	}
 36573  	return JX9_OK;	
 36574  }
 36575  /*
 36576   * string fgetss(resource $handle [, int $length [, string $allowable_tags ]])
 36577   *  Gets line from file pointer and strip HTML tags.
 36578   * Parameters
 36579   * $handle
 36580   *   The file pointer.
 36581   * $length
 36582   *  Reading ends when length - 1 bytes have been read, on a newline
 36583   *  (which is included in the return value), or on EOF (whichever comes first).
 36584   *  If no length is specified, it will keep reading from the stream until it reaches
 36585   *  the end of the line. 
 36586   * $allowable_tags
 36587   *  You can use the optional second parameter to specify tags which should not be stripped. 
 36588   * Return
 36589   *  Returns a string of up to length - 1 bytes read from the file pointed to by 
 36590   *  handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE. 
 36591   */
 36592  static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36593  {
 36594  	const jx9_io_stream *pStream;
 36595  	const char *zLine;
 36596  	io_private *pDev;
 36597  	jx9_int64 n, nLen;
 36598  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36599  		/* Missing/Invalid arguments, return FALSE */
 36600  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36601  		jx9_result_bool(pCtx, 0);
 36602  		return JX9_OK;
 36603  	}
 36604  	/* Extract our private data */
 36605  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36606  	/* Make sure we are dealing with a valid io_private instance */
 36607  	if( IO_PRIVATE_INVALID(pDev) ){
 36608  		/*Expecting an IO handle */
 36609  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36610  		jx9_result_bool(pCtx, 0);
 36611  		return JX9_OK;
 36612  	}
 36613  	/* Point to the target IO stream device */
 36614  	pStream = pDev->pStream;
 36615  	if( pStream == 0  ){
 36616  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36617  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36618  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36619  			);
 36620  		jx9_result_bool(pCtx, 0);
 36621  		return JX9_OK;
 36622  	}
 36623  	nLen = -1;
 36624  	if( nArg > 1 ){
 36625  		/* Maximum data to read */
 36626  		nLen = jx9_value_to_int64(apArg[1]);
 36627  	}
 36628  	/* Perform the requested operation */
 36629  	n = StreamReadLine(pDev, &zLine, nLen);
 36630  	if( n < 1 ){
 36631  		/* EOF or IO error, return FALSE */
 36632  		jx9_result_bool(pCtx, 0);
 36633  	}else{
 36634  		const char *zTaglist = 0;
 36635  		int nTaglen = 0;
 36636  		if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
 36637  			/* Allowed tag */
 36638  			zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
 36639  		}
 36640  		/* Process data just read */
 36641  		jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
 36642  	}
 36643  	return JX9_OK;	
 36644  }
 36645  /*
 36646   * string readdir(resource $dir_handle)
 36647   *   Read entry from directory handle.
 36648   * Parameter
 36649   *  $dir_handle
 36650   *   The directory handle resource previously opened with opendir().
 36651   * Return
 36652   *  Returns the filename on success or FALSE on failure.
 36653   */
 36654  static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36655  {
 36656  	const jx9_io_stream *pStream;
 36657  	io_private *pDev;
 36658  	int rc;
 36659  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36660  		/* Missing/Invalid arguments, return FALSE */
 36661  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36662  		jx9_result_bool(pCtx, 0);
 36663  		return JX9_OK;
 36664  	}
 36665  	/* Extract our private data */
 36666  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36667  	/* Make sure we are dealing with a valid io_private instance */
 36668  	if( IO_PRIVATE_INVALID(pDev) ){
 36669  		/*Expecting an IO handle */
 36670  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36671  		jx9_result_bool(pCtx, 0);
 36672  		return JX9_OK;
 36673  	}
 36674  	/* Point to the target IO stream device */
 36675  	pStream = pDev->pStream;
 36676  	if( pStream == 0  || pStream->xReadDir == 0 ){
 36677  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36678  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36679  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36680  			);
 36681  		jx9_result_bool(pCtx, 0);
 36682  		return JX9_OK;
 36683  	}
 36684  	jx9_result_bool(pCtx, 0);
 36685  	/* Perform the requested operation */
 36686  	rc = pStream->xReadDir(pDev->pHandle, pCtx);
 36687  	if( rc != JX9_OK ){
 36688  		/* Return FALSE */
 36689  		jx9_result_bool(pCtx, 0);
 36690  	}
 36691  	return JX9_OK;
 36692  }
 36693  /*
 36694   * void rewinddir(resource $dir_handle)
 36695   *   Rewind directory handle.
 36696   * Parameter
 36697   *  $dir_handle
 36698   *   The directory handle resource previously opened with opendir().
 36699   * Return
 36700   *  FALSE on failure.
 36701   */
 36702  static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36703  {
 36704  	const jx9_io_stream *pStream;
 36705  	io_private *pDev;
 36706  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36707  		/* Missing/Invalid arguments, return FALSE */
 36708  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36709  		jx9_result_bool(pCtx, 0);
 36710  		return JX9_OK;
 36711  	}
 36712  	/* Extract our private data */
 36713  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36714  	/* Make sure we are dealing with a valid io_private instance */
 36715  	if( IO_PRIVATE_INVALID(pDev) ){
 36716  		/*Expecting an IO handle */
 36717  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36718  		jx9_result_bool(pCtx, 0);
 36719  		return JX9_OK;
 36720  	}
 36721  	/* Point to the target IO stream device */
 36722  	pStream = pDev->pStream;
 36723  	if( pStream == 0  || pStream->xRewindDir == 0 ){
 36724  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36725  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36726  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36727  			);
 36728  		jx9_result_bool(pCtx, 0);
 36729  		return JX9_OK;
 36730  	}
 36731  	/* Perform the requested operation */
 36732  	pStream->xRewindDir(pDev->pHandle);
 36733  	return JX9_OK;
 36734   }
 36735  /* Forward declaration */
 36736  static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
 36737  static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev);
 36738  /*
 36739   * void closedir(resource $dir_handle)
 36740   *   Close directory handle.
 36741   * Parameter
 36742   *  $dir_handle
 36743   *   The directory handle resource previously opened with opendir().
 36744   * Return
 36745   *  FALSE on failure.
 36746   */
 36747  static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36748  {
 36749  	const jx9_io_stream *pStream;
 36750  	io_private *pDev;
 36751  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 36752  		/* Missing/Invalid arguments, return FALSE */
 36753  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36754  		jx9_result_bool(pCtx, 0);
 36755  		return JX9_OK;
 36756  	}
 36757  	/* Extract our private data */
 36758  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 36759  	/* Make sure we are dealing with a valid io_private instance */
 36760  	if( IO_PRIVATE_INVALID(pDev) ){
 36761  		/*Expecting an IO handle */
 36762  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 36763  		jx9_result_bool(pCtx, 0);
 36764  		return JX9_OK;
 36765  	}
 36766  	/* Point to the target IO stream device */
 36767  	pStream = pDev->pStream;
 36768  	if( pStream == 0  || pStream->xCloseDir == 0 ){
 36769  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36770  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 36771  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 36772  			);
 36773  		jx9_result_bool(pCtx, 0);
 36774  		return JX9_OK;
 36775  	}
 36776  	/* Perform the requested operation */
 36777  	pStream->xCloseDir(pDev->pHandle);
 36778  	/* Release the private stucture */
 36779  	ReleaseIOPrivate(pCtx, pDev);
 36780  	jx9MemObjRelease(apArg[0]);
 36781  	return JX9_OK;
 36782   }
 36783  /*
 36784   * resource opendir(string $path[, resource $context])
 36785   *  Open directory handle.
 36786   * Parameters
 36787   * $path
 36788   *   The directory path that is to be opened.
 36789   * $context
 36790   *   A context stream resource.
 36791   * Return
 36792   *  A directory handle resource on success, or FALSE on failure.
 36793   */
 36794  static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36795  {
 36796  	const jx9_io_stream *pStream;
 36797  	const char *zPath;
 36798  	io_private *pDev;
 36799  	int iLen, rc;
 36800  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 36801  		/* Missing/Invalid arguments, return FALSE */
 36802  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
 36803  		jx9_result_bool(pCtx, 0);
 36804  		return JX9_OK;
 36805  	}
 36806  	/* Extract the target path */
 36807  	zPath  = jx9_value_to_string(apArg[0], &iLen);
 36808  	/* Try to extract a stream */
 36809  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
 36810  	if( pStream == 0 ){
 36811  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36812  			"No stream device is associated with the given path(%s)", zPath);
 36813  		jx9_result_bool(pCtx, 0);
 36814  		return JX9_OK;
 36815  	}
 36816  	if( pStream->xOpenDir == 0 ){
 36817  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 36818  			"IO routine(%s) not implemented in the underlying stream(%s) device", 
 36819  			jx9_function_name(pCtx), pStream->zName
 36820  			);
 36821  		jx9_result_bool(pCtx, 0);
 36822  		return JX9_OK;
 36823  	}
 36824  	/* Allocate a new IO private instance */
 36825  	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
 36826  	if( pDev == 0 ){
 36827  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 36828  		jx9_result_bool(pCtx, 0);
 36829  		return JX9_OK;
 36830  	}
 36831  	/* Initialize the structure */
 36832  	InitIOPrivate(pCtx->pVm, pStream, pDev);
 36833  	/* Open the target directory */
 36834  	rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle);
 36835  	if( rc != JX9_OK ){
 36836  		/* IO error, return FALSE */
 36837  		ReleaseIOPrivate(pCtx, pDev);
 36838  		jx9_result_bool(pCtx, 0);
 36839  	}else{
 36840  		/* Return the handle as a resource */
 36841  		jx9_result_resource(pCtx, pDev);
 36842  	}
 36843  	return JX9_OK;
 36844  }
 36845  /*
 36846   * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]])
 36847   *  Reads a file and writes it to the output buffer.
 36848   * Parameters
 36849   *  $filename
 36850   *   The filename being read.
 36851   *  $use_include_path
 36852   *   You can use the optional second parameter and set it to
 36853   *   TRUE, if you want to search for the file in the include_path, too.
 36854   *  $context
 36855   *   A context stream resource.
 36856   * Return
 36857   *  The number of bytes read from the file on success or FALSE on failure.
 36858   */
 36859  static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36860  {
 36861  	int use_include  = FALSE;
 36862  	const jx9_io_stream *pStream;
 36863  	jx9_int64 n, nRead;
 36864  	const char *zFile;
 36865  	char zBuf[8192];
 36866  	void *pHandle;
 36867  	int rc, nLen;
 36868  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 36869  		/* Missing/Invalid arguments, return FALSE */
 36870  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 36871  		jx9_result_bool(pCtx, 0);
 36872  		return JX9_OK;
 36873  	}
 36874  	/* Extract the file path */
 36875  	zFile = jx9_value_to_string(apArg[0], &nLen);
 36876  	/* Point to the target IO stream device */
 36877  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 36878  	if( pStream == 0 ){
 36879  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 36880  		jx9_result_bool(pCtx, 0);
 36881  		return JX9_OK;
 36882  	}
 36883  	if( nArg > 1 ){
 36884  		use_include = jx9_value_to_bool(apArg[1]);
 36885  	}
 36886  	/* Try to open the file in read-only mode */
 36887  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, 
 36888  		use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
 36889  	if( pHandle == 0 ){
 36890  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 36891  		jx9_result_bool(pCtx, 0);
 36892  		return JX9_OK;
 36893  	}
 36894  	/* Perform the requested operation */
 36895  	nRead = 0;
 36896  	for(;;){
 36897  		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
 36898  		if( n < 1 ){
 36899  			/* EOF or IO error, break immediately */
 36900  			break;
 36901  		}
 36902  		/* Output data */
 36903  		rc = jx9_context_output(pCtx, zBuf, (int)n);
 36904  		if( rc == JX9_ABORT ){
 36905  			break;
 36906  		}
 36907  		/* Increment counter */
 36908  		nRead += n;
 36909  	}
 36910  	/* Close the stream */
 36911  	jx9StreamCloseHandle(pStream, pHandle);
 36912  	/* Total number of bytes readen */
 36913  	jx9_result_int64(pCtx, nRead);
 36914  	return JX9_OK;
 36915  }
 36916  /*
 36917   * string file_get_contents(string $filename[, bool $use_include_path = false 
 36918   *         [, resource $context [, int $offset = -1 [, int $maxlen ]]]])
 36919   *  Reads entire file into a string.
 36920   * Parameters
 36921   *  $filename
 36922   *   The filename being read.
 36923   *  $use_include_path
 36924   *   You can use the optional second parameter and set it to
 36925   *   TRUE, if you want to search for the file in the include_path, too.
 36926   *  $context
 36927   *   A context stream resource.
 36928   *  $offset
 36929   *   The offset where the reading starts on the original stream.
 36930   *  $maxlen
 36931   *    Maximum length of data read. The default is to read until end of file 
 36932   *    is reached. Note that this parameter is applied to the stream processed by the filters.
 36933   * Return
 36934   *   The function returns the read data or FALSE on failure.
 36935   */
 36936  static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
 36937  {
 36938  	const jx9_io_stream *pStream;
 36939  	jx9_int64 n, nRead, nMaxlen;
 36940  	int use_include  = FALSE;
 36941  	const char *zFile;
 36942  	char zBuf[8192];
 36943  	void *pHandle;
 36944  	int nLen;
 36945  	
 36946  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 36947  		/* Missing/Invalid arguments, return FALSE */
 36948  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 36949  		jx9_result_bool(pCtx, 0);
 36950  		return JX9_OK;
 36951  	}
 36952  	/* Extract the file path */
 36953  	zFile = jx9_value_to_string(apArg[0], &nLen);
 36954  	/* Point to the target IO stream device */
 36955  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 36956  	if( pStream == 0 ){
 36957  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 36958  		jx9_result_bool(pCtx, 0);
 36959  		return JX9_OK;
 36960  	}
 36961  	nMaxlen = -1;
 36962  	if( nArg > 1 ){
 36963  		use_include = jx9_value_to_bool(apArg[1]);
 36964  	}
 36965  	/* Try to open the file in read-only mode */
 36966  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
 36967  	if( pHandle == 0 ){
 36968  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 36969  		jx9_result_bool(pCtx, 0);
 36970  		return JX9_OK;
 36971  	}
 36972  	if( nArg > 3 ){
 36973  		/* Extract the offset */
 36974  		n = jx9_value_to_int64(apArg[3]);
 36975  		if( n > 0 ){
 36976  			if( pStream->xSeek ){
 36977  				/* Seek to the desired offset */
 36978  				pStream->xSeek(pHandle, n, 0/*SEEK_SET*/);
 36979  			}
 36980  		}
 36981  		if( nArg > 4 ){
 36982  			/* Maximum data to read */
 36983  			nMaxlen = jx9_value_to_int64(apArg[4]);
 36984  		}
 36985  	}
 36986  	/* Perform the requested operation */
 36987  	nRead = 0;
 36988  	for(;;){
 36989  		n = pStream->xRead(pHandle, zBuf, 
 36990  			(nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf));
 36991  		if( n < 1 ){
 36992  			/* EOF or IO error, break immediately */
 36993  			break;
 36994  		}
 36995  		/* Append data */
 36996  		jx9_result_string(pCtx, zBuf, (int)n);
 36997  		/* Increment read counter */
 36998  		nRead += n;
 36999  		if( nMaxlen > 0 && nRead >= nMaxlen ){
 37000  			/* Read limit reached */
 37001  			break;
 37002  		}
 37003  	}
 37004  	/* Close the stream */
 37005  	jx9StreamCloseHandle(pStream, pHandle);
 37006  	/* Check if we have read something */
 37007  	if( jx9_context_result_buf_length(pCtx) < 1 ){
 37008  		/* Nothing read, return FALSE */
 37009  		jx9_result_bool(pCtx, 0);
 37010  	}
 37011  	return JX9_OK;
 37012  }
 37013  /*
 37014   * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]])
 37015   *  Write a string to a file.
 37016   * Parameters
 37017   *  $filename
 37018   *  Path to the file where to write the data.
 37019   * $data
 37020   *  The data to write(Must be a string).
 37021   * $flags
 37022   *  The value of flags can be any combination of the following
 37023   * flags, joined with the binary OR (|) operator.
 37024   *   FILE_USE_INCLUDE_PATH 	Search for filename in the include directory. See include_path for more information.
 37025   *   FILE_APPEND 	        If file filename already exists, append the data to the file instead of overwriting it.
 37026   *   LOCK_EX 	            Acquire an exclusive lock on the file while proceeding to the writing.
 37027   * context
 37028   *  A context stream resource.
 37029   * Return
 37030   *  The function returns the number of bytes that were written to the file, or FALSE on failure. 
 37031   */
 37032  static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37033  {
 37034  	int use_include  = FALSE;
 37035  	const jx9_io_stream *pStream;
 37036  	const char *zFile;
 37037  	const char *zData;
 37038  	int iOpenFlags;
 37039  	void *pHandle;
 37040  	int iFlags;
 37041  	int nLen;
 37042  	
 37043  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
 37044  		/* Missing/Invalid arguments, return FALSE */
 37045  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 37046  		jx9_result_bool(pCtx, 0);
 37047  		return JX9_OK;
 37048  	}
 37049  	/* Extract the file path */
 37050  	zFile = jx9_value_to_string(apArg[0], &nLen);
 37051  	/* Point to the target IO stream device */
 37052  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 37053  	if( pStream == 0 ){
 37054  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 37055  		jx9_result_bool(pCtx, 0);
 37056  		return JX9_OK;
 37057  	}
 37058  	/* Data to write */
 37059  	zData = jx9_value_to_string(apArg[1], &nLen);
 37060  	if( nLen < 1 ){
 37061  		/* Nothing to write, return immediately */
 37062  		jx9_result_bool(pCtx, 0);
 37063  		return JX9_OK;
 37064  	}
 37065  	/* Try to open the file in read-write mode */
 37066  	iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
 37067  	/* Extract the flags */
 37068  	iFlags = 0;
 37069  	if( nArg > 2 ){
 37070  		iFlags = jx9_value_to_int(apArg[2]);
 37071  		if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){
 37072  			use_include = TRUE;
 37073  		}
 37074  		if( iFlags & 0x08 /* FILE_APPEND */){
 37075  			/* If the file already exists, append the data to the file
 37076  			 * instead of overwriting it.
 37077  			 */
 37078  			iOpenFlags &= ~JX9_IO_OPEN_TRUNC;
 37079  			/* Append mode */
 37080  			iOpenFlags |= JX9_IO_OPEN_APPEND;
 37081  		}
 37082  	}
 37083  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include, 
 37084  		nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
 37085  	if( pHandle == 0 ){
 37086  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 37087  		jx9_result_bool(pCtx, 0);
 37088  		return JX9_OK;
 37089  	}
 37090  	if( pStream->xWrite ){
 37091  		jx9_int64 n;
 37092  		if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){
 37093  			/* Try to acquire an exclusive lock */
 37094  			pStream->xLock(pHandle, 1/* LOCK_EX */);
 37095  		}
 37096  		/* Perform the write operation */
 37097  		n = pStream->xWrite(pHandle, (const void *)zData, nLen);
 37098  		if( n < 1 ){
 37099  			/* IO error, return FALSE */
 37100  			jx9_result_bool(pCtx, 0);
 37101  		}else{
 37102  			/* Total number of bytes written */
 37103  			jx9_result_int64(pCtx, n);
 37104  		}
 37105  	}else{
 37106  		/* Read-only stream */
 37107  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, 
 37108  			"Read-only stream(%s): Cannot perform write operation", 
 37109  			pStream ? pStream->zName : "null_stream"
 37110  			);
 37111  		jx9_result_bool(pCtx, 0);
 37112  	}
 37113  	/* Close the handle */
 37114  	jx9StreamCloseHandle(pStream, pHandle);
 37115  	return JX9_OK;
 37116  }
 37117  /*
 37118   * array file(string $filename[, int $flags = 0[, resource $context]])
 37119   *  Reads entire file into an array.
 37120   * Parameters
 37121   *  $filename
 37122   *   The filename being read.
 37123   *  $flags
 37124   *   The optional parameter flags can be one, or more, of the following constants:
 37125   *   FILE_USE_INCLUDE_PATH
 37126   *       Search for the file in the include_path. 
 37127   *   FILE_IGNORE_NEW_LINES
 37128   *       Do not add newline at the end of each array element 
 37129   *   FILE_SKIP_EMPTY_LINES
 37130   *       Skip empty lines 
 37131   *  $context
 37132   *   A context stream resource.
 37133   * Return
 37134   *   The function returns the read data or FALSE on failure.
 37135   */
 37136  static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37137  {
 37138  	const char *zFile, *zPtr, *zEnd, *zBuf;
 37139  	jx9_value *pArray, *pLine;
 37140  	const jx9_io_stream *pStream;
 37141  	int use_include = 0;
 37142  	io_private *pDev;
 37143  	jx9_int64 n;
 37144  	int iFlags;
 37145  	int nLen;
 37146  	
 37147  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 37148  		/* Missing/Invalid arguments, return FALSE */
 37149  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 37150  		jx9_result_bool(pCtx, 0);
 37151  		return JX9_OK;
 37152  	}
 37153  	/* Extract the file path */
 37154  	zFile = jx9_value_to_string(apArg[0], &nLen);
 37155  	/* Point to the target IO stream device */
 37156  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 37157  	if( pStream == 0 ){
 37158  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 37159  		jx9_result_bool(pCtx, 0);
 37160  		return JX9_OK;
 37161  	}
 37162  	/* Allocate a new IO private instance */
 37163  	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
 37164  	if( pDev == 0 ){
 37165  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 37166  		jx9_result_bool(pCtx, 0);
 37167  		return JX9_OK;
 37168  	}
 37169  	/* Initialize the structure */
 37170  	InitIOPrivate(pCtx->pVm, pStream, pDev);
 37171  	iFlags = 0;
 37172  	if( nArg > 1 ){
 37173  		iFlags = jx9_value_to_int(apArg[1]);
 37174  	}
 37175  	if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
 37176  		use_include = TRUE;
 37177  	}
 37178  	/* Create the array and the working value */
 37179  	pArray = jx9_context_new_array(pCtx);
 37180  	pLine = jx9_context_new_scalar(pCtx);
 37181  	if( pArray == 0 || pLine == 0 ){
 37182  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 37183  		jx9_result_bool(pCtx, 0);
 37184  		return JX9_OK;
 37185  	}
 37186  	/* Try to open the file in read-only mode */
 37187  	pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
 37188  	if( pDev->pHandle == 0 ){
 37189  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 37190  		jx9_result_bool(pCtx, 0);
 37191  		/* Don't worry about freeing memory, everything will be released automatically
 37192  		 * as soon we return from this function.
 37193  		 */
 37194  		return JX9_OK;
 37195  	}
 37196  	/* Perform the requested operation */
 37197  	for(;;){
 37198  		/* Try to extract a line */
 37199  		n = StreamReadLine(pDev, &zBuf, -1);
 37200  		if( n < 1 ){
 37201  			/* EOF or IO error */
 37202  			break;
 37203  		}
 37204  		/* Reset the cursor */
 37205  		jx9_value_reset_string_cursor(pLine);
 37206  		/* Remove line ending if requested by the caller */
 37207  		zPtr = zBuf;
 37208  		zEnd = &zBuf[n];
 37209  		if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){
 37210  			/* Ignore trailig lines */
 37211  			while( zPtr < zEnd && (zEnd[-1] == '\n' 
 37212  #ifdef __WINNT__
 37213  				|| zEnd[-1] == '\r'
 37214  #endif
 37215  				)){
 37216  					n--;
 37217  					zEnd--;
 37218  			}
 37219  		}
 37220  		if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){
 37221  			/* Ignore empty lines */
 37222  			while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){
 37223  				zPtr++;
 37224  			}
 37225  			if( zPtr >= zEnd ){
 37226  				/* Empty line */
 37227  				continue;
 37228  			}
 37229  		}
 37230  		jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
 37231  		/* Insert line */
 37232  		jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
 37233  	}
 37234  	/* Close the stream */
 37235  	jx9StreamCloseHandle(pStream, pDev->pHandle);
 37236  	/* Release the io_private instance */
 37237  	ReleaseIOPrivate(pCtx, pDev);
 37238  	/* Return the created array */
 37239  	jx9_result_value(pCtx, pArray);
 37240  	return JX9_OK;
 37241  }
 37242  /*
 37243   * bool copy(string $source, string $dest[, resource $context ] )
 37244   *  Makes a copy of the file source to dest.
 37245   * Parameters
 37246   *  $source
 37247   *   Path to the source file.
 37248   *  $dest
 37249   *   The destination path. If dest is a URL, the copy operation 
 37250   *   may fail if the wrapper does not support overwriting of existing files. 
 37251   *  $context
 37252   *   A context stream resource.
 37253   * Return
 37254   *  TRUE on success or FALSE on failure.
 37255   */
 37256  static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37257  {
 37258  	const jx9_io_stream *pSin, *pSout;	
 37259  	const char *zFile;
 37260  	char zBuf[8192];
 37261  	void *pIn, *pOut;
 37262  	jx9_int64 n;
 37263  	int nLen;
 37264  	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
 37265  		/* Missing/Invalid arguments, return FALSE */
 37266  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
 37267  		jx9_result_bool(pCtx, 0);
 37268  		return JX9_OK;
 37269  	}
 37270  	/* Extract the source name */
 37271  	zFile = jx9_value_to_string(apArg[0], &nLen);
 37272  	/* Point to the target IO stream device */
 37273  	pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 37274  	if( pSin == 0 ){
 37275  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 37276  		jx9_result_bool(pCtx, 0);
 37277  		return JX9_OK;
 37278  	}
 37279  	/* Try to open the source file in a read-only mode */
 37280  	pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
 37281  	if( pIn == 0 ){
 37282  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
 37283  		jx9_result_bool(pCtx, 0);
 37284  		return JX9_OK;
 37285  	}
 37286  	/* Extract the destination name */
 37287  	zFile = jx9_value_to_string(apArg[1], &nLen);
 37288  	/* Point to the target IO stream device */
 37289  	pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 37290  	if( pSout == 0 ){
 37291  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 37292  		jx9_result_bool(pCtx, 0);
 37293  		jx9StreamCloseHandle(pSin, pIn);
 37294  		return JX9_OK;
 37295  	}
 37296  	if( pSout->xWrite == 0 ){
 37297  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37298  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37299  			jx9_function_name(pCtx), pSin->zName
 37300  			);
 37301  		jx9_result_bool(pCtx, 0);
 37302  		jx9StreamCloseHandle(pSin, pIn);
 37303  		return JX9_OK;
 37304  	}
 37305  	/* Try to open the destination file in a read-write mode */
 37306  	pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile, 
 37307  		JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
 37308  	if( pOut == 0 ){
 37309  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
 37310  		jx9_result_bool(pCtx, 0);
 37311  		jx9StreamCloseHandle(pSin, pIn);
 37312  		return JX9_OK;
 37313  	}
 37314  	/* Perform the requested operation */
 37315  	for(;;){
 37316  		/* Read from source */
 37317  		n = pSin->xRead(pIn, zBuf, sizeof(zBuf));
 37318  		if( n < 1 ){
 37319  			/* EOF or IO error, break immediately */
 37320  			break;
 37321  		}
 37322  		/* Write to dest */
 37323  		n = pSout->xWrite(pOut, zBuf, n);
 37324  		if( n < 1 ){
 37325  			/* IO error, break immediately */
 37326  			break;
 37327  		}
 37328  	}
 37329  	/* Close the streams */
 37330  	jx9StreamCloseHandle(pSin, pIn);
 37331  	jx9StreamCloseHandle(pSout, pOut);
 37332  	/* Return TRUE */
 37333  	jx9_result_bool(pCtx, 1);
 37334  	return JX9_OK;
 37335  }
 37336  /*
 37337   * array fstat(resource $handle)
 37338   *  Gets information about a file using an open file pointer.
 37339   * Parameters
 37340   *  $handle
 37341   *   The file pointer.
 37342   * Return
 37343   *  Returns an array with the statistics of the file or FALSE on failure.
 37344   */
 37345  static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37346  {
 37347  	jx9_value *pArray, *pValue;
 37348  	const jx9_io_stream *pStream;
 37349  	io_private *pDev;
 37350  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 37351  		/* Missing/Invalid arguments, return FALSE */
 37352  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37353  		jx9_result_bool(pCtx, 0);
 37354  		return JX9_OK;
 37355  	}
 37356  	/* Extract our private data */
 37357  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37358  	/* Make sure we are dealing with a valid io_private instance */
 37359  	if( IO_PRIVATE_INVALID(pDev) ){
 37360  		/* Expecting an IO handle */
 37361  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37362  		jx9_result_bool(pCtx, 0);
 37363  		return JX9_OK;
 37364  	}
 37365  	/* Point to the target IO stream device */
 37366  	pStream = pDev->pStream;
 37367  	if( pStream == 0  || pStream->xStat == 0){
 37368  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37369  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37370  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 37371  			);
 37372  		jx9_result_bool(pCtx, 0);
 37373  		return JX9_OK;
 37374  	}
 37375  	/* Create the array and the working value */
 37376  	pArray = jx9_context_new_array(pCtx);
 37377  	pValue = jx9_context_new_scalar(pCtx);
 37378  	if( pArray == 0 || pValue == 0 ){
 37379  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 37380  		jx9_result_bool(pCtx, 0);
 37381  		return JX9_OK;
 37382  	}
 37383  	/* Perform the requested operation */
 37384  	pStream->xStat(pDev->pHandle, pArray, pValue);
 37385  	/* Return the freshly created array */
 37386  	jx9_result_value(pCtx, pArray);
 37387  	/* Don't worry about freeing memory here, everything will be
 37388  	 * released automatically as soon we return from this function.
 37389  	 */
 37390  	return JX9_OK;
 37391  }
 37392  /*
 37393   * int fwrite(resource $handle, string $string[, int $length])
 37394   *  Writes the contents of string to the file stream pointed to by handle.
 37395   * Parameters
 37396   *  $handle
 37397   *   The file pointer.
 37398   *  $string
 37399   *   The string that is to be written.
 37400   *  $length
 37401   *   If the length argument is given, writing will stop after length bytes have been written
 37402   *   or the end of string is reached, whichever comes first. 
 37403   * Return
 37404   *  Returns the number of bytes written, or FALSE on error.
 37405   */
 37406  static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37407  {
 37408  	const jx9_io_stream *pStream;
 37409  	const char *zString;
 37410  	io_private *pDev;
 37411  	int nLen, n;
 37412  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
 37413  		/* Missing/Invalid arguments, return FALSE */
 37414  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37415  		jx9_result_bool(pCtx, 0);
 37416  		return JX9_OK;
 37417  	}
 37418  	/* Extract our private data */
 37419  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37420  	/* Make sure we are dealing with a valid io_private instance */
 37421  	if( IO_PRIVATE_INVALID(pDev) ){
 37422  		/* Expecting an IO handle */
 37423  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37424  		jx9_result_bool(pCtx, 0);
 37425  		return JX9_OK;
 37426  	}
 37427  	/* Point to the target IO stream device */
 37428  	pStream = pDev->pStream;
 37429  	if( pStream == 0  || pStream->xWrite == 0){
 37430  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37431  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37432  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 37433  			);
 37434  		jx9_result_bool(pCtx, 0);
 37435  		return JX9_OK;
 37436  	}
 37437  	/* Extract the data to write */
 37438  	zString = jx9_value_to_string(apArg[1], &nLen);
 37439  	if( nArg > 2 ){
 37440  		/* Maximum data length to write */
 37441  		n = jx9_value_to_int(apArg[2]);
 37442  		if( n >= 0 && n < nLen ){
 37443  			nLen = n;
 37444  		}
 37445  	}
 37446  	if( nLen < 1 ){
 37447  		/* Nothing to write */
 37448  		jx9_result_int(pCtx, 0);
 37449  		return JX9_OK;
 37450  	}
 37451  	/* Perform the requested operation */
 37452  	n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
 37453  	if( n <  0 ){
 37454  		/* IO error, return FALSE */
 37455  		jx9_result_bool(pCtx, 0);
 37456  	}else{
 37457  		/* #Bytes written */
 37458  		jx9_result_int(pCtx, n);
 37459  	}
 37460  	return JX9_OK;
 37461  }
 37462  /*
 37463   * bool flock(resource $handle, int $operation)
 37464   *  Portable advisory file locking.
 37465   * Parameters
 37466   *  $handle
 37467   *   The file pointer.
 37468   *  $operation
 37469   *   operation is one of the following:
 37470   *      LOCK_SH to acquire a shared lock (reader).
 37471   *      LOCK_EX to acquire an exclusive lock (writer).
 37472   *      LOCK_UN to release a lock (shared or exclusive).
 37473   * Return
 37474   *  Returns TRUE on success or FALSE on failure.
 37475   */
 37476  static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37477  {
 37478  	const jx9_io_stream *pStream;
 37479  	io_private *pDev;
 37480  	int nLock;
 37481  	int rc;
 37482  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
 37483  		/* Missing/Invalid arguments, return FALSE */
 37484  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37485  		jx9_result_bool(pCtx, 0);
 37486  		return JX9_OK;
 37487  	}
 37488  	/* Extract our private data */
 37489  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37490  	/* Make sure we are dealing with a valid io_private instance */
 37491  	if( IO_PRIVATE_INVALID(pDev) ){
 37492  		/*Expecting an IO handle */
 37493  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37494  		jx9_result_bool(pCtx, 0);
 37495  		return JX9_OK;
 37496  	}
 37497  	/* Point to the target IO stream device */
 37498  	pStream = pDev->pStream;
 37499  	if( pStream == 0  || pStream->xLock == 0){
 37500  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37501  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37502  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 37503  			);
 37504  		jx9_result_bool(pCtx, 0);
 37505  		return JX9_OK;
 37506  	}
 37507  	/* Requested lock operation */
 37508  	nLock = jx9_value_to_int(apArg[1]);
 37509  	/* Lock operation */
 37510  	rc = pStream->xLock(pDev->pHandle, nLock);
 37511  	/* IO result */
 37512  	jx9_result_bool(pCtx, rc == JX9_OK);
 37513  	return JX9_OK;
 37514  }
 37515  /*
 37516   * int fpassthru(resource $handle)
 37517   *  Output all remaining data on a file pointer.
 37518   * Parameters
 37519   *  $handle
 37520   *   The file pointer. 
 37521   * Return
 37522   *  Total number of characters read from handle and passed through
 37523   *  to the output on success or FALSE on failure.
 37524   */
 37525  static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37526  {
 37527  	const jx9_io_stream *pStream;
 37528  	io_private *pDev;
 37529  	jx9_int64 n, nRead;
 37530  	char zBuf[8192];
 37531  	int rc;
 37532  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 37533  		/* Missing/Invalid arguments, return FALSE */
 37534  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37535  		jx9_result_bool(pCtx, 0);
 37536  		return JX9_OK;
 37537  	}
 37538  	/* Extract our private data */
 37539  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37540  	/* Make sure we are dealing with a valid io_private instance */
 37541  	if( IO_PRIVATE_INVALID(pDev) ){
 37542  		/*Expecting an IO handle */
 37543  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37544  		jx9_result_bool(pCtx, 0);
 37545  		return JX9_OK;
 37546  	}
 37547  	/* Point to the target IO stream device */
 37548  	pStream = pDev->pStream;
 37549  	if( pStream == 0  ){
 37550  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37551  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37552  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 37553  			);
 37554  		jx9_result_bool(pCtx, 0);
 37555  		return JX9_OK;
 37556  	}
 37557  	/* Perform the requested operation */
 37558  	nRead = 0;
 37559  	for(;;){
 37560  		n = StreamRead(pDev, zBuf, sizeof(zBuf));
 37561  		if( n < 1 ){
 37562  			/* Error or EOF */
 37563  			break;
 37564  		}
 37565  		/* Increment the read counter */
 37566  		nRead += n;
 37567  		/* Output data */
 37568  		rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
 37569  		if( rc == JX9_ABORT ){
 37570  			/* Consumer callback request an operation abort */
 37571  			break;
 37572  		}
 37573  	}
 37574  	/* Total number of bytes readen */
 37575  	jx9_result_int64(pCtx, nRead);
 37576  	return JX9_OK;
 37577  }
 37578  /* CSV reader/writer private data */
 37579  struct csv_data
 37580  {
 37581  	int delimiter;    /* Delimiter. Default ', ' */
 37582  	int enclosure;    /* Enclosure. Default '"'*/
 37583  	io_private *pDev; /* Open stream handle */
 37584  	int iCount;       /* Counter */
 37585  };
 37586  /* 
 37587   * The following callback is used by the fputcsv() function inorder to iterate
 37588   * throw array entries and output CSV data based on the current key and it's
 37589   * associated data.
 37590   */
 37591  static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
 37592  {
 37593  	struct csv_data *pData = (struct csv_data *)pUserData;
 37594  	const char *zData;
 37595  	int nLen, c2;
 37596  	sxu32 n;
 37597  	/* Point to the raw data */
 37598  	zData = jx9_value_to_string(pValue, &nLen);
 37599  	if( nLen < 1 ){
 37600  		/* Nothing to write */
 37601  		return JX9_OK;
 37602  	}
 37603  	if( pData->iCount > 0 ){
 37604  		/* Write the delimiter */
 37605  		pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char));
 37606  	}
 37607  	n = 1;
 37608  	c2 = 0;
 37609  	if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK || 
 37610  		SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){
 37611  			c2 = 1;
 37612  			if( n == 0 ){
 37613  				c2 = 2;
 37614  			}
 37615  			/* Write the enclosure */
 37616  			pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
 37617  			if( c2 > 1 ){
 37618  				pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
 37619  			}
 37620  	}
 37621  	/* Write the data */
 37622  	if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){
 37623  		SXUNUSED(pKey); /* cc warning */
 37624  		return JX9_ABORT;
 37625  	}
 37626  	if( c2 > 0 ){
 37627  		/* Write the enclosure */
 37628  		pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
 37629  		if( c2 > 1 ){
 37630  			pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
 37631  		}
 37632  	}
 37633  	pData->iCount++;
 37634  	return JX9_OK;
 37635  }
 37636  /*
 37637   * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]])
 37638   *  Format line as CSV and write to file pointer.
 37639   * Parameters
 37640   *  $handle
 37641   *   Open file handle.
 37642   * $fields
 37643   *   An array of values.
 37644   * $delimiter
 37645   *   The optional delimiter parameter sets the field delimiter (one character only).
 37646   * $enclosure
 37647   *  The optional enclosure parameter sets the field enclosure (one character only).
 37648   */
 37649  static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37650  {
 37651  	const jx9_io_stream *pStream;
 37652  	struct csv_data sCsv;
 37653  	io_private *pDev;
 37654  	char *zEol;
 37655  	int eolen;
 37656  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
 37657  		/* Missing/Invalid arguments, return FALSE */
 37658  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
 37659  		jx9_result_bool(pCtx, 0);
 37660  		return JX9_OK;
 37661  	}
 37662  	/* Extract our private data */
 37663  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37664  	/* Make sure we are dealing with a valid io_private instance */
 37665  	if( IO_PRIVATE_INVALID(pDev) ){
 37666  		/*Expecting an IO handle */
 37667  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37668  		jx9_result_bool(pCtx, 0);
 37669  		return JX9_OK;
 37670  	}
 37671  	/* Point to the target IO stream device */
 37672  	pStream = pDev->pStream;
 37673  	if( pStream == 0  || pStream->xWrite == 0){
 37674  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37675  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 37676  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 37677  			);
 37678  		jx9_result_bool(pCtx, 0);
 37679  		return JX9_OK;
 37680  	}
 37681  	/* Set default csv separator */
 37682  	sCsv.delimiter = ',';
 37683  	sCsv.enclosure = '"';
 37684  	sCsv.pDev = pDev;
 37685  	sCsv.iCount = 0;
 37686  	if( nArg > 2 ){
 37687  		/* User delimiter */
 37688  		const char *z;
 37689  		int n;
 37690  		z = jx9_value_to_string(apArg[2], &n);
 37691  		if( n > 0 ){
 37692  			sCsv.delimiter = z[0];
 37693  		}
 37694  		if( nArg > 3 ){
 37695  			z = jx9_value_to_string(apArg[3], &n);
 37696  			if( n > 0 ){
 37697  				sCsv.enclosure = z[0];
 37698  			}
 37699  		}
 37700  	}
 37701  	/* Iterate throw array entries and write csv data */
 37702  	jx9_array_walk(apArg[1], csv_write_callback, &sCsv);
 37703  	/* Write a line ending */
 37704  #ifdef __WINNT__
 37705  	zEol = "\r\n";
 37706  	eolen = (int)sizeof("\r\n")-1;
 37707  #else
 37708  	/* Assume UNIX LF */
 37709  	zEol = "\n";
 37710  	eolen = (int)sizeof(char);
 37711  #endif
 37712  	pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen);
 37713  	return JX9_OK;
 37714  }
 37715  /*
 37716   * fprintf, vfprintf private data.
 37717   * An instance of the following structure is passed to the formatted
 37718   * input consumer callback defined below.
 37719   */
 37720  typedef struct fprintf_data fprintf_data;
 37721  struct fprintf_data
 37722  {
 37723  	io_private *pIO;        /* IO stream */
 37724  	jx9_int64 nCount;       /* Total number of bytes written */
 37725  };
 37726  /*
 37727   * Callback [i.e: Formatted input consumer] for the fprintf function.
 37728   */
 37729  static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
 37730  {
 37731  	fprintf_data *pFdata = (fprintf_data *)pUserData;
 37732  	jx9_int64 n;
 37733  	/* Write the formatted data */
 37734  	n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen);
 37735  	if( n < 1 ){
 37736  		SXUNUSED(pCtx); /* cc warning */
 37737  		/* IO error, abort immediately */
 37738  		return SXERR_ABORT;
 37739  	}
 37740  	/* Increment counter */
 37741  	pFdata->nCount += n;
 37742  	return JX9_OK;
 37743  }
 37744  /*
 37745   * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]])
 37746   *  Write a formatted string to a stream.
 37747   * Parameters
 37748   *  $handle
 37749   *   The file pointer.
 37750   *  $format
 37751   *   String format (see sprintf()).
 37752   * Return
 37753   *  The length of the written string.
 37754   */
 37755  static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37756  {
 37757  	fprintf_data sFdata;
 37758  	const char *zFormat;
 37759  	io_private *pDev;
 37760  	int nLen;
 37761  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
 37762  		/* Missing/Invalid arguments, return zero */
 37763  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
 37764  		jx9_result_int(pCtx, 0);
 37765  		return JX9_OK;
 37766  	}
 37767  	/* Extract our private data */
 37768  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37769  	/* Make sure we are dealing with a valid io_private instance */
 37770  	if( IO_PRIVATE_INVALID(pDev) ){
 37771  		/*Expecting an IO handle */
 37772  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37773  		jx9_result_int(pCtx, 0);
 37774  		return JX9_OK;
 37775  	}
 37776  	/* Point to the target IO stream device */
 37777  	if( pDev->pStream == 0  || pDev->pStream->xWrite == 0 ){
 37778  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37779  			"IO routine(%s) not implemented in the underlying stream(%s) device", 
 37780  			jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
 37781  			);
 37782  		jx9_result_int(pCtx, 0);
 37783  		return JX9_OK;
 37784  	}
 37785  	/* Extract the string format */
 37786  	zFormat = jx9_value_to_string(apArg[1], &nLen);
 37787  	if( nLen < 1 ){
 37788  		/* Empty string, return zero */
 37789  		jx9_result_int(pCtx, 0);
 37790  		return JX9_OK;
 37791  	}
 37792  	/* Prepare our private data */
 37793  	sFdata.nCount = 0;
 37794  	sFdata.pIO = pDev;
 37795  	/* Format the string */
 37796  	jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
 37797  	/* Return total number of bytes written */
 37798  	jx9_result_int64(pCtx, sFdata.nCount);
 37799  	return JX9_OK;
 37800  }
 37801  /*
 37802   * int vfprintf(resource $handle, string $format, array $args)
 37803   *  Write a formatted string to a stream.
 37804   * Parameters
 37805   *  $handle
 37806   *   The file pointer.
 37807   *  $format
 37808   *   String format (see sprintf()).
 37809   * $args
 37810   *   User arguments.
 37811   * Return
 37812   *  The length of the written string.
 37813   */
 37814  static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
 37815  {
 37816  	fprintf_data sFdata;
 37817  	const char *zFormat;
 37818  	jx9_hashmap *pMap;
 37819  	io_private *pDev;
 37820  	SySet sArg;
 37821  	int n, nLen;
 37822  	if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1])  || !jx9_value_is_json_array(apArg[2]) ){
 37823  		/* Missing/Invalid arguments, return zero */
 37824  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
 37825  		jx9_result_int(pCtx, 0);
 37826  		return JX9_OK;
 37827  	}
 37828  	/* Extract our private data */
 37829  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 37830  	/* Make sure we are dealing with a valid io_private instance */
 37831  	if( IO_PRIVATE_INVALID(pDev) ){
 37832  		/*Expecting an IO handle */
 37833  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 37834  		jx9_result_int(pCtx, 0);
 37835  		return JX9_OK;
 37836  	}
 37837  	/* Point to the target IO stream device */
 37838  	if( pDev->pStream == 0  || pDev->pStream->xWrite == 0 ){
 37839  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 37840  			"IO routine(%s) not implemented in the underlying stream(%s) device", 
 37841  			jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
 37842  			);
 37843  		jx9_result_int(pCtx, 0);
 37844  		return JX9_OK;
 37845  	}
 37846  	/* Extract the string format */
 37847  	zFormat = jx9_value_to_string(apArg[1], &nLen);
 37848  	if( nLen < 1 ){
 37849  		/* Empty string, return zero */
 37850  		jx9_result_int(pCtx, 0);
 37851  		return JX9_OK;
 37852  	}
 37853  	/* Point to hashmap */
 37854  	pMap = (jx9_hashmap *)apArg[2]->x.pOther;
 37855  	/* Extract arguments from the hashmap */
 37856  	n = jx9HashmapValuesToSet(pMap, &sArg);
 37857  	/* Prepare our private data */
 37858  	sFdata.nCount = 0;
 37859  	sFdata.pIO = pDev;
 37860  	/* Format the string */
 37861  	jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
 37862  	/* Return total number of bytes written*/
 37863  	jx9_result_int64(pCtx, sFdata.nCount);
 37864  	SySetRelease(&sArg);
 37865  	return JX9_OK;
 37866  }
 37867  /*
 37868   * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
 37869   * According to the JX9 reference manual:
 37870   *  The mode parameter specifies the type of access you require to the stream. It may be any of the following
 37871   *   'r' 	Open for reading only; place the file pointer at the beginning of the file.
 37872   *   'r+' 	Open for reading and writing; place the file pointer at the beginning of the file.
 37873   *   'w' 	Open for writing only; place the file pointer at the beginning of the file and truncate the file
 37874   *          to zero length. If the file does not exist, attempt to create it.
 37875   *   'w+' 	Open for reading and writing; place the file pointer at the beginning of the file and truncate
 37876   *              the file to zero length. If the file does not exist, attempt to create it.
 37877   *   'a' 	Open for writing only; place the file pointer at the end of the file. If the file does not 
 37878   *         exist, attempt to create it.
 37879   *   'a+' 	Open for reading and writing; place the file pointer at the end of the file. If the file does 
 37880   *          not exist, attempt to create it.
 37881   *   'x' 	Create and open for writing only; place the file pointer at the beginning of the file. If the file
 37882   *         already exists, 
 37883   *         the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file
 37884   *         does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for
 37885   *         the underlying open(2) system call.
 37886   *   'x+' 	Create and open for reading and writing; otherwise it has the same behavior as 'x'.
 37887   *   'c' 	Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated
 37888   *          (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer
 37889   *          is positioned on the beginning of the file.
 37890   *          This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file
 37891   *          as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can
 37892   *          be used after the lock is requested).
 37893   *   'c+' 	Open the file for reading and writing; otherwise it has the same behavior as 'c'. 
 37894   */
 37895  static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen)
 37896  {
 37897  	const char *zEnd = &zMode[nLen];
 37898  	int iFlag = 0;
 37899  	int c;
 37900  	if( nLen < 1 ){
 37901  		/* Open in a read-only mode */
 37902  		return JX9_IO_OPEN_RDONLY;
 37903  	}
 37904  	c = zMode[0];
 37905  	if( c == 'r' || c == 'R' ){
 37906  		/* Read-only access */
 37907  		iFlag = JX9_IO_OPEN_RDONLY;
 37908  		zMode++; /* Advance */
 37909  		if( zMode < zEnd ){
 37910  			c = zMode[0];
 37911  			if( c == '+' || c == 'w' || c == 'W' ){
 37912  				/* Read+Write access */
 37913  				iFlag = JX9_IO_OPEN_RDWR;
 37914  			}
 37915  		}
 37916  	}else if( c == 'w' || c == 'W' ){
 37917  		/* Overwrite mode.
 37918  		 * If the file does not exists, try to create it
 37919  		 */
 37920  		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
 37921  		zMode++; /* Advance */
 37922  		if( zMode < zEnd ){
 37923  			c = zMode[0];
 37924  			if( c == '+' || c == 'r' || c == 'R' ){
 37925  				/* Read+Write access */
 37926  				iFlag &= ~JX9_IO_OPEN_WRONLY;
 37927  				iFlag |= JX9_IO_OPEN_RDWR;
 37928  			}
 37929  		}
 37930  	}else if( c == 'a' || c == 'A' ){
 37931  		/* Append mode (place the file pointer at the end of the file).
 37932  		 * Create the file if it does not exists.
 37933  		 */
 37934  		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
 37935  		zMode++; /* Advance */
 37936  		if( zMode < zEnd ){
 37937  			c = zMode[0];
 37938  			if( c == '+' ){
 37939  				/* Read-Write access */
 37940  				iFlag &= ~JX9_IO_OPEN_WRONLY;
 37941  				iFlag |= JX9_IO_OPEN_RDWR;
 37942  			}
 37943  		}
 37944  	}else if( c == 'x' || c == 'X' ){
 37945  		/* Exclusive access.
 37946  		 * If the file already exists, return immediately with a failure code.
 37947  		 * Otherwise create a new file.
 37948  		 */
 37949  		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
 37950  		zMode++; /* Advance */
 37951  		if( zMode < zEnd ){
 37952  			c = zMode[0];
 37953  			if( c == '+' || c == 'r' || c == 'R' ){
 37954  				/* Read-Write access */
 37955  				iFlag &= ~JX9_IO_OPEN_WRONLY;
 37956  				iFlag |= JX9_IO_OPEN_RDWR;
 37957  			}
 37958  		}
 37959  	}else if( c == 'c' || c == 'C' ){
 37960  		/* Overwrite mode.Create the file if it does not exists.*/
 37961  		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
 37962  		zMode++; /* Advance */
 37963  		if( zMode < zEnd ){
 37964  			c = zMode[0];
 37965  			if( c == '+' ){
 37966  				/* Read-Write access */
 37967  				iFlag &= ~JX9_IO_OPEN_WRONLY;
 37968  				iFlag |= JX9_IO_OPEN_RDWR;
 37969  			}
 37970  		}
 37971  	}else{
 37972  		/* Invalid mode. Assume a read only open */
 37973  		jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
 37974  		iFlag = JX9_IO_OPEN_RDONLY;
 37975  	}
 37976  	while( zMode < zEnd ){
 37977  		c = zMode[0];
 37978  		if( c == 'b' || c == 'B' ){
 37979  			iFlag &= ~JX9_IO_OPEN_TEXT;
 37980  			iFlag |= JX9_IO_OPEN_BINARY;
 37981  		}else if( c == 't' || c == 'T' ){
 37982  			iFlag &= ~JX9_IO_OPEN_BINARY;
 37983  			iFlag |= JX9_IO_OPEN_TEXT;
 37984  		}
 37985  		zMode++;
 37986  	}
 37987  	return iFlag;
 37988  }
 37989  /*
 37990   * Initialize the IO private structure.
 37991   */
 37992  static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut)
 37993  {
 37994  	pOut->pStream = pStream;
 37995  	SyBlobInit(&pOut->sBuffer, &pVm->sAllocator);
 37996  	pOut->nOfft = 0;
 37997  	/* Set the magic number */
 37998  	pOut->iMagic = IO_PRIVATE_MAGIC;
 37999  }
 38000  /*
 38001   * Release the IO private structure.
 38002   */
 38003  static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev)
 38004  {
 38005  	SyBlobRelease(&pDev->sBuffer);
 38006  	pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
 38007  	/* Release the whole structure */
 38008  	jx9_context_free_chunk(pCtx, pDev);
 38009  }
 38010  /*
 38011   * Reset the IO private structure.
 38012   */
 38013  static void ResetIOPrivate(io_private *pDev)
 38014  {
 38015  	SyBlobReset(&pDev->sBuffer);
 38016  	pDev->nOfft = 0;
 38017  }
 38018  /* Forward declaration */
 38019  static int is_jx9_stream(const jx9_io_stream *pStream);
 38020  /*
 38021   * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]])
 38022   *  Open a file, a URL or any other IO stream.
 38023   * Parameters
 38024   *  $filename
 38025   *   If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search
 38026   *   for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given
 38027   *   then a regular file is assumed.
 38028   *  $mode
 38029   *   The mode parameter specifies the type of access you require to the stream
 38030   *   See the block comment associated with the StrModeToFlags() for the supported 
 38031   *   modes.
 38032   *  $use_include_path
 38033   *   You can use the optional second parameter and set it to
 38034   *   TRUE, if you want to search for the file in the include_path, too.
 38035   *  $context
 38036   *   A context stream resource.
 38037   * Return
 38038   *  File handle on success or FALSE on failure.
 38039   */
 38040  static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38041  {
 38042  	const jx9_io_stream *pStream;
 38043  	const char *zUri, *zMode;
 38044  	jx9_value *pResource;
 38045  	io_private *pDev;
 38046  	int iLen, imLen;
 38047  	int iOpenFlags;
 38048  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 38049  		/* Missing/Invalid arguments, return FALSE */
 38050  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
 38051  		jx9_result_bool(pCtx, 0);
 38052  		return JX9_OK;
 38053  	}
 38054  	/* Extract the URI and the desired access mode */
 38055  	zUri  = jx9_value_to_string(apArg[0], &iLen);
 38056  	if( nArg > 1 ){
 38057  		zMode = jx9_value_to_string(apArg[1], &imLen);
 38058  	}else{
 38059  		/* Set a default read-only mode */
 38060  		zMode = "r";
 38061  		imLen = (int)sizeof(char);
 38062  	}
 38063  	/* Try to extract a stream */
 38064  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
 38065  	if( pStream == 0 ){
 38066  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 38067  			"No stream device is associated with the given URI(%s)", zUri);
 38068  		jx9_result_bool(pCtx, 0);
 38069  		return JX9_OK;
 38070  	}
 38071  	/* Allocate a new IO private instance */
 38072  	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
 38073  	if( pDev == 0 ){
 38074  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 38075  		jx9_result_bool(pCtx, 0);
 38076  		return JX9_OK;
 38077  	}
 38078  	pResource = 0;
 38079  	if( nArg > 3 ){
 38080  		pResource = apArg[3];
 38081  	}else if( is_jx9_stream(pStream) ){
 38082  		/* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
 38083  		 * virtual machine.
 38084  		 */
 38085  		pResource = apArg[0];
 38086  	}
 38087  	/* Initialize the structure */
 38088  	InitIOPrivate(pCtx->pVm, pStream, pDev);
 38089  	/* Convert open mode to JX9 flags */
 38090  	iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
 38091  	/* Try to get a handle */
 38092  	pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags, 
 38093  		nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
 38094  	if( pDev->pHandle == 0 ){
 38095  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
 38096  		jx9_result_bool(pCtx, 0);
 38097  		jx9_context_free_chunk(pCtx, pDev);
 38098  		return JX9_OK;
 38099  	}
 38100  	/* All done, return the io_private instance as a resource */
 38101  	jx9_result_resource(pCtx, pDev);
 38102  	return JX9_OK;
 38103  }
 38104  /*
 38105   * bool fclose(resource $handle)
 38106   *  Closes an open file pointer
 38107   * Parameters
 38108   *  $handle
 38109   *   The file pointer. 
 38110   * Return
 38111   *  TRUE on success or FALSE on failure.
 38112   */
 38113  static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38114  {
 38115  	const jx9_io_stream *pStream;
 38116  	io_private *pDev;
 38117  	jx9_vm *pVm;
 38118  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38119  		/* Missing/Invalid arguments, return FALSE */
 38120  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 38121  		jx9_result_bool(pCtx, 0);
 38122  		return JX9_OK;
 38123  	}
 38124  	/* Extract our private data */
 38125  	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
 38126  	/* Make sure we are dealing with a valid io_private instance */
 38127  	if( IO_PRIVATE_INVALID(pDev) ){
 38128  		/*Expecting an IO handle */
 38129  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
 38130  		jx9_result_bool(pCtx, 0);
 38131  		return JX9_OK;
 38132  	}
 38133  	/* Point to the target IO stream device */
 38134  	pStream = pDev->pStream;
 38135  	if( pStream == 0 ){
 38136  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, 
 38137  			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", 
 38138  			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
 38139  			);
 38140  		jx9_result_bool(pCtx, 0);
 38141  		return JX9_OK;
 38142  	}
 38143  	/* Point to the VM that own this context */
 38144  	pVm = pCtx->pVm;
 38145  	/* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */
 38146  	if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){
 38147  		/* Perform the requested operation */
 38148  		jx9StreamCloseHandle(pStream, pDev->pHandle);
 38149  		/* Release the IO private structure */
 38150  		ReleaseIOPrivate(pCtx, pDev);
 38151  		/* Invalidate the resource handle */
 38152  		jx9_value_release(apArg[0]);
 38153  	}
 38154  	/* Return TRUE */
 38155  	jx9_result_bool(pCtx, 1);
 38156  	return JX9_OK;
 38157  }
 38158  #if !defined(JX9_DISABLE_HASH_FUNC)
 38159  /*
 38160   * MD5/SHA1 digest consumer.
 38161   */
 38162  static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
 38163  {
 38164  	/* Append hex chunk verbatim */
 38165  	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
 38166  	return SXRET_OK;
 38167  }
 38168  /*
 38169   * string md5_file(string $uri[, bool $raw_output = false ])
 38170   *  Calculates the md5 hash of a given file.
 38171   * Parameters
 38172   *  $uri
 38173   *   Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
 38174   *  $raw_output
 38175   *   When TRUE, returns the digest in raw binary format with a length of 16.
 38176   * Return
 38177   *  Return the MD5 digest on success or FALSE on failure.
 38178   */
 38179  static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38180  {
 38181  	const jx9_io_stream *pStream;
 38182  	unsigned char zDigest[16];
 38183  	int raw_output  = FALSE;
 38184  	const char *zFile;
 38185  	MD5Context sCtx;
 38186  	char zBuf[8192];
 38187  	void *pHandle;
 38188  	jx9_int64 n;
 38189  	int nLen;
 38190  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 38191  		/* Missing/Invalid arguments, return FALSE */
 38192  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 38193  		jx9_result_bool(pCtx, 0);
 38194  		return JX9_OK;
 38195  	}
 38196  	/* Extract the file path */
 38197  	zFile = jx9_value_to_string(apArg[0], &nLen);
 38198  	/* Point to the target IO stream device */
 38199  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 38200  	if( pStream == 0 ){
 38201  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 38202  		jx9_result_bool(pCtx, 0);
 38203  		return JX9_OK;
 38204  	}
 38205  	if( nArg > 1 ){
 38206  		raw_output = jx9_value_to_bool(apArg[1]);
 38207  	}
 38208  	/* Try to open the file in read-only mode */
 38209  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
 38210  	if( pHandle == 0 ){
 38211  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 38212  		jx9_result_bool(pCtx, 0);
 38213  		return JX9_OK;
 38214  	}
 38215  	/* Init the MD5 context */
 38216  	MD5Init(&sCtx);
 38217  	/* Perform the requested operation */ 
 38218  	for(;;){
 38219  		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
 38220  		if( n < 1 ){
 38221  			/* EOF or IO error, break immediately */
 38222  			break;
 38223  		}
 38224  		MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
 38225  	}
 38226  	/* Close the stream */
 38227  	jx9StreamCloseHandle(pStream, pHandle);
 38228  	/* Extract the digest */
 38229  	MD5Final(zDigest, &sCtx);
 38230  	if( raw_output ){
 38231  		/* Output raw digest */
 38232  		jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
 38233  	}else{
 38234  		/* Perform a binary to hex conversion */
 38235  		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
 38236  	}
 38237  	return JX9_OK;
 38238  }
 38239  /*
 38240   * string sha1_file(string $uri[, bool $raw_output = false ])
 38241   *  Calculates the SHA1 hash of a given file.
 38242   * Parameters
 38243   *  $uri
 38244   *   Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
 38245   *  $raw_output
 38246   *   When TRUE, returns the digest in raw binary format with a length of 20.
 38247   * Return
 38248   *  Return the SHA1 digest on success or FALSE on failure.
 38249   */
 38250  static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38251  {
 38252  	const jx9_io_stream *pStream;
 38253  	unsigned char zDigest[20];
 38254  	int raw_output  = FALSE;
 38255  	const char *zFile;
 38256  	SHA1Context sCtx;
 38257  	char zBuf[8192];
 38258  	void *pHandle;
 38259  	jx9_int64 n;
 38260  	int nLen;
 38261  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 38262  		/* Missing/Invalid arguments, return FALSE */
 38263  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 38264  		jx9_result_bool(pCtx, 0);
 38265  		return JX9_OK;
 38266  	}
 38267  	/* Extract the file path */
 38268  	zFile = jx9_value_to_string(apArg[0], &nLen);
 38269  	/* Point to the target IO stream device */
 38270  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 38271  	if( pStream == 0 ){
 38272  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 38273  		jx9_result_bool(pCtx, 0);
 38274  		return JX9_OK;
 38275  	}
 38276  	if( nArg > 1 ){
 38277  		raw_output = jx9_value_to_bool(apArg[1]);
 38278  	}
 38279  	/* Try to open the file in read-only mode */
 38280  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
 38281  	if( pHandle == 0 ){
 38282  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 38283  		jx9_result_bool(pCtx, 0);
 38284  		return JX9_OK;
 38285  	}
 38286  	/* Init the SHA1 context */
 38287  	SHA1Init(&sCtx);
 38288  	/* Perform the requested operation */ 
 38289  	for(;;){
 38290  		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
 38291  		if( n < 1 ){
 38292  			/* EOF or IO error, break immediately */
 38293  			break;
 38294  		}
 38295  		SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
 38296  	}
 38297  	/* Close the stream */
 38298  	jx9StreamCloseHandle(pStream, pHandle);
 38299  	/* Extract the digest */
 38300  	SHA1Final(&sCtx, zDigest);
 38301  	if( raw_output ){
 38302  		/* Output raw digest */
 38303  		jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
 38304  	}else{
 38305  		/* Perform a binary to hex conversion */
 38306  		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
 38307  	}
 38308  	return JX9_OK;
 38309  }
 38310  #endif /* JX9_DISABLE_HASH_FUNC */
 38311  /*
 38312   * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] )
 38313   *  Parse a configuration file.
 38314   * Parameters
 38315   * $filename
 38316   *  The filename of the ini file being parsed.
 38317   * $process_sections
 38318   *  By setting the process_sections parameter to TRUE, you get a multidimensional array
 38319   *  with the section names and settings included.
 38320   *  The default for process_sections is FALSE.
 38321   * $scanner_mode
 38322   *  Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW.
 38323   *  If INI_SCANNER_RAW is supplied, then option values will not be parsed.
 38324   * Return
 38325   *  The settings are returned as an associative array on success.
 38326   *  Otherwise is returned.
 38327   */
 38328  static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38329  {
 38330  	const jx9_io_stream *pStream;
 38331  	const char *zFile;
 38332  	SyBlob sContents;
 38333  	void *pHandle;
 38334  	int nLen;
 38335  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 38336  		/* Missing/Invalid arguments, return FALSE */
 38337  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 38338  		jx9_result_bool(pCtx, 0);
 38339  		return JX9_OK;
 38340  	}
 38341  	/* Extract the file path */
 38342  	zFile = jx9_value_to_string(apArg[0], &nLen);
 38343  	/* Point to the target IO stream device */
 38344  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 38345  	if( pStream == 0 ){
 38346  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 38347  		jx9_result_bool(pCtx, 0);
 38348  		return JX9_OK;
 38349  	}
 38350  	/* Try to open the file in read-only mode */
 38351  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
 38352  	if( pHandle == 0 ){
 38353  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 38354  		jx9_result_bool(pCtx, 0);
 38355  		return JX9_OK;
 38356  	}
 38357  	SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
 38358  	/* Read the whole file */
 38359  	jx9StreamReadWholeFile(pHandle, pStream, &sContents);
 38360  	if( SyBlobLength(&sContents) < 1 ){
 38361  		/* Empty buffer, return FALSE */
 38362  		jx9_result_bool(pCtx, 0);
 38363  	}else{
 38364  		/* Process the raw INI buffer */
 38365  		jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents), 
 38366  			nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
 38367  	}
 38368  	/* Close the stream */
 38369  	jx9StreamCloseHandle(pStream, pHandle);
 38370  	/* Release the working buffer */
 38371  	SyBlobRelease(&sContents);
 38372  	return JX9_OK;
 38373  }
 38374  /*
 38375   * Section:
 38376   *    ZIP archive processing.
 38377   * Authors:
 38378   *    Symisc Systems, devel@symisc.net.
 38379   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 38380   * Status:
 38381   *    Stable.
 38382   */
 38383  typedef struct zip_raw_data zip_raw_data;
 38384  struct zip_raw_data
 38385  {
 38386  	int iType;         /* Where the raw data is stored */
 38387  	union raw_data{
 38388  		struct mmap_data{
 38389  			void *pMap;          /* Memory mapped data */
 38390  			jx9_int64 nSize;     /* Map size */
 38391  			const jx9_vfs *pVfs; /* Underlying vfs */
 38392  		}mmap;
 38393  		SyBlob sBlob;  /* Memory buffer */
 38394  	}raw;
 38395  };
 38396  #define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */
 38397  #define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically
 38398                                 * allocated memory chunk.
 38399  							   */
 38400   /*
 38401    * mixed zip_open(string $filename)
 38402    *  Opens a new zip archive for reading.
 38403    * Parameters
 38404    *  $filename
 38405    *   The file name of the ZIP archive to open.
 38406    * Return
 38407    *  A resource handle for later use with zip_read() and zip_close() or FALSE on failure.
 38408    */
 38409  static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38410  {
 38411  	const jx9_io_stream *pStream;
 38412  	SyArchive *pArchive;
 38413  	zip_raw_data *pRaw;
 38414  	const char *zFile;
 38415  	SyBlob *pContents;
 38416  	void *pHandle;
 38417  	int nLen;
 38418  	sxi32 rc;
 38419  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 38420  		/* Missing/Invalid arguments, return FALSE */
 38421  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
 38422  		jx9_result_bool(pCtx, 0);
 38423  		return JX9_OK;
 38424  	}
 38425  	/* Extract the file path */
 38426  	zFile = jx9_value_to_string(apArg[0], &nLen);
 38427  	/* Point to the target IO stream device */
 38428  	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
 38429  	if( pStream == 0 ){
 38430  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
 38431  		jx9_result_bool(pCtx, 0);
 38432  		return JX9_OK;
 38433  	}
 38434  	/* Create an in-memory archive */
 38435  	pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
 38436  	if( pArchive == 0 ){
 38437  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
 38438  		jx9_result_bool(pCtx, 0);
 38439  		return JX9_OK;
 38440  	}
 38441  	pRaw = (zip_raw_data *)&pArchive[1];
 38442  	/* Initialize the archive */
 38443  	SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0);
 38444  	/* Extract the default stream */
 38445  	if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){
 38446  		const jx9_vfs *pVfs;
 38447  		/* Try to get a memory view of the whole file since ZIP files
 38448  		 * tends to be very big this days, this is a huge performance win.
 38449  		 */
 38450  		pVfs = jx9ExportBuiltinVfs();
 38451  		if( pVfs && pVfs->xMmap ){
 38452  			rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
 38453  			if( rc == JX9_OK ){
 38454  				/* Nice, Extract the whole archive */
 38455  				rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize);
 38456  				if( rc != SXRET_OK ){
 38457  					if( pVfs->xUnmap ){
 38458  						pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
 38459  					}
 38460  					/* Release the allocated chunk */
 38461  					jx9_context_free_chunk(pCtx, pArchive);
 38462  					/* Something goes wrong with this ZIP archive, return FALSE */
 38463  					jx9_result_bool(pCtx, 0);
 38464  					return JX9_OK;
 38465  				}
 38466  				/* Archive successfully opened */
 38467  				pRaw->iType = ZIP_RAW_DATA_MMAPED;
 38468  				pRaw->raw.mmap.pVfs = pVfs;
 38469  				goto success;
 38470  			}
 38471  		}
 38472  		/* FALL THROUGH */
 38473  	}
 38474  	/* Try to open the file in read-only mode */
 38475  	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
 38476  	if( pHandle == 0 ){
 38477  		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
 38478  		jx9_result_bool(pCtx, 0);
 38479  		return JX9_OK;
 38480  	}
 38481  	pContents = &pRaw->raw.sBlob;
 38482  	SyBlobInit(pContents, &pCtx->pVm->sAllocator);
 38483  	/* Read the whole file */
 38484  	jx9StreamReadWholeFile(pHandle, pStream, pContents);
 38485  	/* Assume an invalid ZIP file */
 38486  	rc = SXERR_INVALID;
 38487  	if( SyBlobLength(pContents) > 0 ){
 38488  		/* Extract archive entries */
 38489  		rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents));
 38490  	}
 38491  	pRaw->iType = ZIP_RAW_DATA_MEMBUF;
 38492  	/* Close the stream */
 38493  	jx9StreamCloseHandle(pStream, pHandle);
 38494  	if( rc != SXRET_OK ){
 38495  		/* Release the working buffer */
 38496  		SyBlobRelease(pContents);
 38497  		/* Release the allocated chunk */
 38498  		jx9_context_free_chunk(pCtx, pArchive);
 38499  		/* Something goes wrong with this ZIP archive, return FALSE */
 38500  		jx9_result_bool(pCtx, 0);
 38501  		return JX9_OK;
 38502  	}
 38503  success:
 38504  	/* Reset the loop cursor */
 38505  	SyArchiveResetLoopCursor(pArchive);
 38506  	/* Return the in-memory archive as a resource handle */
 38507  	jx9_result_resource(pCtx, pArchive);
 38508  	return JX9_OK;
 38509  }
 38510  /*
 38511    * void zip_close(resource $zip)
 38512    *  Close an in-memory ZIP archive.
 38513    * Parameters
 38514    *  $zip
 38515    *   A ZIP file previously opened with zip_open().
 38516    * Return
 38517    *  null.
 38518    */
 38519  static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38520  {
 38521  	SyArchive *pArchive;
 38522  	zip_raw_data *pRaw;
 38523  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38524  		/* Missing/Invalid arguments */
 38525  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38526  		return JX9_OK;
 38527  	}
 38528  	/* Point to the in-memory archive */
 38529  	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
 38530  	/* Make sure we are dealing with a valid ZIP archive */
 38531  	if( SXARCH_INVALID(pArchive) ){
 38532  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38533  		return JX9_OK;
 38534  	}
 38535  	/* Release the archive */
 38536  	SyArchiveRelease(pArchive);
 38537  	pRaw = (zip_raw_data *)&pArchive[1];
 38538  	if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
 38539  		SyBlobRelease(&pRaw->raw.sBlob);
 38540  	}else{
 38541  		const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs;
 38542  		if( pVfs->xUnmap ){
 38543  			/* Unmap the memory view */
 38544  			pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
 38545  		}
 38546  	}
 38547  	/* Release the memory chunk */
 38548  	jx9_context_free_chunk(pCtx, pArchive);
 38549  	return JX9_OK;
 38550  }
 38551  /*
 38552    * mixed zip_read(resource $zip)
 38553    *  Reads the next entry from an in-memory ZIP archive.
 38554    * Parameters
 38555    *  $zip
 38556    *   A ZIP file previously opened with zip_open().
 38557    * Return
 38558    *  A directory entry resource for later use with the zip_entry_... functions
 38559    *  or FALSE if there are no more entries to read, or an error code if an error occurred.
 38560    */
 38561  static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38562  {
 38563  	SyArchiveEntry *pNext = 0; /* cc warning */
 38564  	SyArchive *pArchive;
 38565  	sxi32 rc;
 38566  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38567  		/* Missing/Invalid arguments */
 38568  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38569  		/* return FALSE */
 38570  		jx9_result_bool(pCtx, 0);
 38571  		return JX9_OK;
 38572  	}
 38573  	/* Point to the in-memory archive */
 38574  	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
 38575  	/* Make sure we are dealing with a valid ZIP archive */
 38576  	if( SXARCH_INVALID(pArchive) ){
 38577  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38578  		/* return FALSE */
 38579  		jx9_result_bool(pCtx, 0);
 38580  		return JX9_OK;
 38581  	}
 38582  	/* Extract the next entry */
 38583  	rc = SyArchiveGetNextEntry(pArchive, &pNext);
 38584  	if( rc != SXRET_OK ){
 38585  		/* No more entries in the central directory, return FALSE */
 38586  		jx9_result_bool(pCtx, 0);
 38587  	}else{
 38588  		/* Return as a resource handle */
 38589  		jx9_result_resource(pCtx, pNext);
 38590  		/* Point to the ZIP raw data */
 38591  		pNext->pUserData = (void *)&pArchive[1];
 38592  	}
 38593  	return JX9_OK;
 38594  }
 38595  /*
 38596    * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ])
 38597    *  Open a directory entry for reading
 38598    * Parameters
 38599    *  $zip
 38600    *   A ZIP file previously opened with zip_open().
 38601    *  $zip_entry
 38602    *   A directory entry returned by zip_read().
 38603    * $mode
 38604    *   Not used
 38605    * Return
 38606    *  A directory entry resource for later use with the zip_entry_... functions
 38607    *  or FALSE if there are no more entries to read, or an error code if an error occurred.
 38608    */
 38609  static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38610  {
 38611  	SyArchiveEntry *pEntry;
 38612  	SyArchive *pArchive;
 38613  	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
 38614  		/* Missing/Invalid arguments */
 38615  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38616  		/* return FALSE */
 38617  		jx9_result_bool(pCtx, 0);
 38618  		return JX9_OK;
 38619  	}
 38620  	/* Point to the in-memory archive */
 38621  	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
 38622  	/* Make sure we are dealing with a valid ZIP archive */
 38623  	if( SXARCH_INVALID(pArchive) ){
 38624  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
 38625  		/* return FALSE */
 38626  		jx9_result_bool(pCtx, 0);
 38627  		return JX9_OK;
 38628  	}
 38629  	/* Make sure we are dealing with a valid ZIP archive entry */
 38630  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
 38631  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38632  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38633  		/* return FALSE */
 38634  		jx9_result_bool(pCtx, 0);
 38635  		return JX9_OK;
 38636  	}
 38637  	/* All done. Actually this function is a no-op, return TRUE */
 38638  	jx9_result_bool(pCtx, 1);
 38639  	return JX9_OK;
 38640  }
 38641  /*
 38642    * bool zip_entry_close(resource $zip_entry)
 38643    *  Close a directory entry.
 38644    * Parameters
 38645    *  $zip_entry
 38646    *   A directory entry returned by zip_read().
 38647    * Return
 38648    *  Returns TRUE on success or FALSE on failure.
 38649    */
 38650  static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38651  {
 38652  	SyArchiveEntry *pEntry;
 38653  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38654  		/* Missing/Invalid arguments */
 38655  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38656  		/* return FALSE */
 38657  		jx9_result_bool(pCtx, 0);
 38658  		return JX9_OK;
 38659  	}
 38660  	/* Make sure we are dealing with a valid ZIP archive entry */
 38661  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38662  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38663  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38664  		/* return FALSE */
 38665  		jx9_result_bool(pCtx, 0);
 38666  		return JX9_OK;
 38667  	}
 38668  	/* Reset the read cursor */
 38669  	pEntry->nReadCount = 0;
 38670  	/*All done. Actually this function is a no-op, return TRUE */
 38671  	jx9_result_bool(pCtx, 1);
 38672  	return JX9_OK;
 38673  }
 38674  /*
 38675    * string zip_entry_name(resource $zip_entry)
 38676    *  Retrieve the name of a directory entry.
 38677    * Parameters
 38678    *  $zip_entry
 38679    *   A directory entry returned by zip_read().
 38680    * Return
 38681    *  The name of the directory entry.
 38682    */
 38683  static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38684  {
 38685  	SyArchiveEntry *pEntry;
 38686  	SyString *pName;
 38687  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38688  		/* Missing/Invalid arguments */
 38689  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38690  		/* return FALSE */
 38691  		jx9_result_bool(pCtx, 0);
 38692  		return JX9_OK;
 38693  	}
 38694  	/* Make sure we are dealing with a valid ZIP archive entry */
 38695  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38696  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38697  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38698  		/* return FALSE */
 38699  		jx9_result_bool(pCtx, 0);
 38700  		return JX9_OK;
 38701  	}
 38702  	/* Return entry name */
 38703  	pName = &pEntry->sFileName;
 38704  	jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
 38705  	return JX9_OK;
 38706  }
 38707  /*
 38708    * int64 zip_entry_filesize(resource $zip_entry)
 38709    *  Retrieve the actual file size of a directory entry.
 38710    * Parameters
 38711    *  $zip_entry
 38712    *   A directory entry returned by zip_read().
 38713    * Return
 38714    *  The size of the directory entry.
 38715    */
 38716  static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38717  {
 38718  	SyArchiveEntry *pEntry;
 38719  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38720  		/* Missing/Invalid arguments */
 38721  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38722  		/* return FALSE */
 38723  		jx9_result_bool(pCtx, 0);
 38724  		return JX9_OK;
 38725  	}
 38726  	/* Make sure we are dealing with a valid ZIP archive entry */
 38727  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38728  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38729  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38730  		/* return FALSE */
 38731  		jx9_result_bool(pCtx, 0);
 38732  		return JX9_OK;
 38733  	}
 38734  	/* Return entry size */
 38735  	jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
 38736  	return JX9_OK;
 38737  }
 38738  /*
 38739    * int64 zip_entry_compressedsize(resource $zip_entry)
 38740    *  Retrieve the compressed size of a directory entry.
 38741    * Parameters
 38742    *  $zip_entry
 38743    *   A directory entry returned by zip_read().
 38744    * Return
 38745    *  The compressed size.
 38746    */
 38747  static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38748  {
 38749  	SyArchiveEntry *pEntry;
 38750  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38751  		/* Missing/Invalid arguments */
 38752  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38753  		/* return FALSE */
 38754  		jx9_result_bool(pCtx, 0);
 38755  		return JX9_OK;
 38756  	}
 38757  	/* Make sure we are dealing with a valid ZIP archive entry */
 38758  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38759  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38760  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38761  		/* return FALSE */
 38762  		jx9_result_bool(pCtx, 0);
 38763  		return JX9_OK;
 38764  	}
 38765  	/* Return entry compressed size */
 38766  	jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
 38767  	return JX9_OK;
 38768  }
 38769  /*
 38770    * string zip_entry_read(resource $zip_entry[, int $length])
 38771    *  Reads from an open directory entry.
 38772    * Parameters
 38773    *  $zip_entry
 38774    *   A directory entry returned by zip_read().
 38775    *  $length
 38776    *   The number of bytes to return. If not specified, this function
 38777    *   will attempt to read 1024 bytes.
 38778    * Return
 38779    *  Returns the data read, or FALSE if the end of the file is reached.
 38780    */
 38781  static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38782  {
 38783  	SyArchiveEntry *pEntry;
 38784  	zip_raw_data *pRaw;
 38785  	const char *zData;
 38786  	int iLength;
 38787  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38788  		/* Missing/Invalid arguments */
 38789  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38790  		/* return FALSE */
 38791  		jx9_result_bool(pCtx, 0);
 38792  		return JX9_OK;
 38793  	}
 38794  	/* Make sure we are dealing with a valid ZIP archive entry */
 38795  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38796  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38797  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38798  		/* return FALSE */
 38799  		jx9_result_bool(pCtx, 0);
 38800  		return JX9_OK;
 38801  	}
 38802  	zData = 0;
 38803  	if( pEntry->nReadCount >= pEntry->nByteCompr ){
 38804  		/* No more data to read, return FALSE */
 38805  		jx9_result_bool(pCtx, 0);
 38806  		return JX9_OK;
 38807  	}
 38808  	/* Set a default read length */
 38809  	iLength = 1024;
 38810  	if( nArg > 1 ){
 38811  		iLength = jx9_value_to_int(apArg[1]);
 38812  		if( iLength < 1 ){
 38813  			iLength = 1024;
 38814  		}
 38815  	}
 38816  	if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
 38817  		iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
 38818  	}
 38819  	/* Return the entry contents */
 38820  	pRaw = (zip_raw_data *)pEntry->pUserData;
 38821  	if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
 38822  		zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
 38823  	}else{
 38824  		const char *zMap = (const char *)pRaw->raw.mmap.pMap;
 38825  		/* Memory mmaped chunk */
 38826  		zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
 38827  	}
 38828  	/* Increment the read counter */
 38829  	pEntry->nReadCount += iLength;
 38830  	/* Return the raw data */
 38831  	jx9_result_string(pCtx, zData, iLength);
 38832  	return JX9_OK;
 38833  }
 38834  /*
 38835    * bool zip_entry_reset_cursor(resource $zip_entry)
 38836    *  Reset the read cursor of an open directory entry.
 38837    * Parameters
 38838    *  $zip_entry
 38839    *   A directory entry returned by zip_read().
 38840    * Return
 38841    *  TRUE on success, FALSE on failure.
 38842    */
 38843  static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38844  {
 38845  	SyArchiveEntry *pEntry;
 38846  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38847  		/* Missing/Invalid arguments */
 38848  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38849  		/* return FALSE */
 38850  		jx9_result_bool(pCtx, 0);
 38851  		return JX9_OK;
 38852  	}
 38853  	/* Make sure we are dealing with a valid ZIP archive entry */
 38854  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38855  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38856  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38857  		/* return FALSE */
 38858  		jx9_result_bool(pCtx, 0);
 38859  		return JX9_OK;
 38860  	}
 38861  	/* Reset the cursor */
 38862  	pEntry->nReadCount = 0;
 38863  	/* Return TRUE */
 38864  	jx9_result_bool(pCtx, 1);
 38865  	return JX9_OK;
 38866  }
 38867  /*
 38868    * string zip_entry_compressionmethod(resource $zip_entry)
 38869    *  Retrieve the compression method of a directory entry.
 38870    * Parameters
 38871    *  $zip_entry
 38872    *   A directory entry returned by zip_read().
 38873    * Return
 38874    *  The compression method on success or FALSE on failure.
 38875    */
 38876  static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
 38877  {
 38878  	SyArchiveEntry *pEntry;
 38879  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 38880  		/* Missing/Invalid arguments */
 38881  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38882  		/* return FALSE */
 38883  		jx9_result_bool(pCtx, 0);
 38884  		return JX9_OK;
 38885  	}
 38886  	/* Make sure we are dealing with a valid ZIP archive entry */
 38887  	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
 38888  	if( SXARCH_ENTRY_INVALID(pEntry) ){
 38889  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
 38890  		/* return FALSE */
 38891  		jx9_result_bool(pCtx, 0);
 38892  		return JX9_OK;
 38893  	}
 38894  	switch(pEntry->nComprMeth){
 38895  	case 0:
 38896  		/* No compression;entry is stored */
 38897  		jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
 38898  		break;
 38899  	case 8:
 38900  		/* Entry is deflated (Default compression algorithm)  */
 38901  		jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
 38902  		break;
 38903  		/* Exotic compression algorithms */ 
 38904  	case 1:
 38905  		jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
 38906  		break;
 38907  	case 2:
 38908  	case 3:
 38909  	case 4:
 38910  	case 5:
 38911  		/* Entry is reduced */
 38912  		jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
 38913  		break;
 38914  	case 6:
 38915  		/* Entry is imploded */
 38916  		jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
 38917  		break;
 38918  	default:
 38919  		jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
 38920  		break;
 38921  	}
 38922  	return JX9_OK;
 38923  }
 38924  #endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
 38925  /* NULL VFS [i.e: a no-op VFS]*/
 38926  static const jx9_vfs null_vfs = {
 38927  	"null_vfs", 
 38928  	JX9_VFS_VERSION, 
 38929  	0, /* int (*xChdir)(const char *) */
 38930  	0, /* int (*xChroot)(const char *); */
 38931  	0, /* int (*xGetcwd)(jx9_context *) */
 38932  	0, /* int (*xMkdir)(const char *, int, int) */
 38933  	0, /* int (*xRmdir)(const char *) */ 
 38934  	0, /* int (*xIsdir)(const char *) */
 38935  	0, /* int (*xRename)(const char *, const char *) */
 38936  	0, /*int (*xRealpath)(const char *, jx9_context *)*/
 38937  	0, /* int (*xSleep)(unsigned int) */
 38938  	0, /* int (*xUnlink)(const char *) */
 38939  	0, /* int (*xFileExists)(const char *) */
 38940  	0, /*int (*xChmod)(const char *, int)*/
 38941  	0, /*int (*xChown)(const char *, const char *)*/
 38942  	0, /*int (*xChgrp)(const char *, const char *)*/
 38943  	0, /* jx9_int64 (*xFreeSpace)(const char *) */
 38944  	0, /* jx9_int64 (*xTotalSpace)(const char *) */
 38945  	0, /* jx9_int64 (*xFileSize)(const char *) */
 38946  	0, /* jx9_int64 (*xFileAtime)(const char *) */
 38947  	0, /* jx9_int64 (*xFileMtime)(const char *) */
 38948  	0, /* jx9_int64 (*xFileCtime)(const char *) */
 38949  	0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
 38950  	0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
 38951  	0, /* int (*xIsfile)(const char *) */
 38952  	0, /* int (*xIslink)(const char *) */
 38953  	0, /* int (*xReadable)(const char *) */
 38954  	0, /* int (*xWritable)(const char *) */
 38955  	0, /* int (*xExecutable)(const char *) */
 38956  	0, /* int (*xFiletype)(const char *, jx9_context *) */
 38957  	0, /* int (*xGetenv)(const char *, jx9_context *) */
 38958  	0, /* int (*xSetenv)(const char *, const char *) */ 
 38959  	0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
 38960  	0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
 38961  	0, /* void (*xUnmap)(void *, jx9_int64);  */
 38962  	0, /* int (*xLink)(const char *, const char *, int) */
 38963  	0, /* int (*xUmask)(int) */
 38964  	0, /* void (*xTempDir)(jx9_context *) */
 38965  	0, /* unsigned int (*xProcessId)(void) */
 38966  	0, /* int (*xUid)(void) */
 38967  	0, /* int (*xGid)(void) */
 38968  	0, /* void (*xUsername)(jx9_context *) */
 38969  	0  /* int (*xExec)(const char *, jx9_context *) */
 38970  };
 38971  #ifndef JX9_DISABLE_BUILTIN_FUNC
 38972  #ifndef JX9_DISABLE_DISK_IO
 38973  #ifdef __WINNT__
 38974  /*
 38975   * Windows VFS implementation for the JX9 engine.
 38976   * Authors:
 38977   *    Symisc Systems, devel@symisc.net.
 38978   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 38979   * Status:
 38980   *    Stable.
 38981   */
 38982  /* What follows here is code that is specific to windows systems. */
 38983  #include <Windows.h>
 38984  /*
 38985  ** Convert a UTF-8 string to microsoft unicode (UTF-16?).
 38986  **
 38987  ** Space to hold the returned string is obtained from HeapAlloc().
 38988  ** Taken from the sqlite3 source tree
 38989  ** status: Public Domain
 38990  */
 38991  static WCHAR *jx9utf8ToUnicode(const char *zFilename){
 38992    int nChar;
 38993    WCHAR *zWideFilename;
 38994  
 38995    nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
 38996    zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
 38997    if( zWideFilename == 0 ){
 38998   	return 0;
 38999    } 
 39000    nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
 39001    if( nChar==0 ){
 39002      HeapFree(GetProcessHeap(), 0, zWideFilename);
 39003      return 0;
 39004    }
 39005    return zWideFilename;
 39006  }
 39007  /*
 39008  ** Convert a UTF-8 filename into whatever form the underlying
 39009  ** operating system wants filenames in.Space to hold the result
 39010  ** is obtained from HeapAlloc() and must be freed by the calling
 39011  ** function.
 39012  ** Taken from the sqlite3 source tree
 39013  ** status: Public Domain
 39014  */
 39015  static void *jx9convertUtf8Filename(const char *zFilename){
 39016    void *zConverted;
 39017    zConverted = jx9utf8ToUnicode(zFilename);
 39018    return zConverted;
 39019  }
 39020  /*
 39021  ** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
 39022  ** obtained from HeapAlloc().
 39023  ** Taken from the sqlite3 source tree
 39024  ** status: Public Domain
 39025  */
 39026  static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){
 39027    char *zFilename;
 39028    int nByte;
 39029  
 39030    nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
 39031    zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
 39032    if( zFilename == 0 ){
 39033    	return 0;
 39034    }
 39035    nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
 39036    if( nByte == 0 ){
 39037      HeapFree(GetProcessHeap(), 0, zFilename);
 39038      return 0;
 39039    }
 39040    return zFilename;
 39041  }
 39042  /* int (*xchdir)(const char *) */
 39043  static int WinVfs_chdir(const char *zPath)
 39044  {
 39045  	void * pConverted;
 39046  	BOOL rc;
 39047  	pConverted = jx9convertUtf8Filename(zPath);
 39048  	if( pConverted == 0 ){
 39049  		return -1;
 39050  	}
 39051  	rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
 39052  	HeapFree(GetProcessHeap(), 0, pConverted);
 39053  	return rc ? JX9_OK : -1;
 39054  }
 39055  /* int (*xGetcwd)(jx9_context *) */
 39056  static int WinVfs_getcwd(jx9_context *pCtx)
 39057  {
 39058  	WCHAR zDir[2048];
 39059  	char *zConverted;
 39060  	DWORD rc;
 39061  	/* Get the current directory */
 39062  	rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
 39063  	if( rc < 1 ){
 39064  		return -1;
 39065  	}
 39066  	zConverted = jx9unicodeToUtf8(zDir);
 39067  	if( zConverted == 0 ){
 39068  		return -1;
 39069  	}
 39070  	jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
 39071  	HeapFree(GetProcessHeap(), 0, zConverted);
 39072  	return JX9_OK;
 39073  }
 39074  /* int (*xMkdir)(const char *, int, int) */
 39075  static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
 39076  {
 39077  	void * pConverted;
 39078  	BOOL rc;
 39079  	pConverted = jx9convertUtf8Filename(zPath);
 39080  	if( pConverted == 0 ){
 39081  		return -1;
 39082  	}
 39083  	mode= 0; /* MSVC warning */
 39084  	recursive = 0;
 39085  	rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
 39086  	HeapFree(GetProcessHeap(), 0, pConverted);
 39087  	return rc ? JX9_OK : -1;
 39088  }
 39089  /* int (*xRmdir)(const char *) */
 39090  static int WinVfs_rmdir(const char *zPath)
 39091  {
 39092  	void * pConverted;
 39093  	BOOL rc;
 39094  	pConverted = jx9convertUtf8Filename(zPath);
 39095  	if( pConverted == 0 ){
 39096  		return -1;
 39097  	}
 39098  	rc = RemoveDirectoryW((LPCWSTR)pConverted);
 39099  	HeapFree(GetProcessHeap(), 0, pConverted);
 39100  	return rc ? JX9_OK : -1;
 39101  }
 39102  /* int (*xIsdir)(const char *) */
 39103  static int WinVfs_isdir(const char *zPath)
 39104  {
 39105  	void * pConverted;
 39106  	DWORD dwAttr;
 39107  	pConverted = jx9convertUtf8Filename(zPath);
 39108  	if( pConverted == 0 ){
 39109  		return -1;
 39110  	}
 39111  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39112  	HeapFree(GetProcessHeap(), 0, pConverted);
 39113  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39114  		return -1;
 39115  	}
 39116  	return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
 39117  }
 39118  /* int (*xRename)(const char *, const char *) */
 39119  static int WinVfs_Rename(const char *zOld, const char *zNew)
 39120  {
 39121  	void *pOld, *pNew;
 39122  	BOOL rc = 0;
 39123  	pOld = jx9convertUtf8Filename(zOld);
 39124  	if( pOld == 0 ){
 39125  		return -1;
 39126  	}
 39127  	pNew = jx9convertUtf8Filename(zNew);
 39128  	if( pNew  ){
 39129  		rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
 39130  	}
 39131  	HeapFree(GetProcessHeap(), 0, pOld);
 39132  	if( pNew ){
 39133  		HeapFree(GetProcessHeap(), 0, pNew);
 39134  	}
 39135  	return rc ? JX9_OK : - 1;
 39136  }
 39137  /* int (*xRealpath)(const char *, jx9_context *) */
 39138  static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
 39139  {
 39140  	WCHAR zTemp[2048];
 39141  	void *pPath;
 39142  	char *zReal;
 39143  	DWORD n;
 39144  	pPath = jx9convertUtf8Filename(zPath);
 39145  	if( pPath == 0 ){
 39146  		return -1;
 39147  	}
 39148  	n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
 39149  	if( n > 0 ){
 39150  		if( n >= sizeof(zTemp) ){
 39151  			n = sizeof(zTemp) - 1;
 39152  		}
 39153  		GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
 39154  	}
 39155  	HeapFree(GetProcessHeap(), 0, pPath);
 39156  	if( !n ){
 39157  		return -1;
 39158  	}
 39159  	zReal = jx9unicodeToUtf8(zTemp);
 39160  	if( zReal == 0 ){
 39161  		return -1;
 39162  	}
 39163  	jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
 39164  	HeapFree(GetProcessHeap(), 0, zReal);
 39165  	return JX9_OK;
 39166  }
 39167  /* int (*xSleep)(unsigned int) */
 39168  static int WinVfs_Sleep(unsigned int uSec)
 39169  {
 39170  	Sleep(uSec/1000/*uSec per Millisec */);
 39171  	return JX9_OK;
 39172  }
 39173  /* int (*xUnlink)(const char *) */
 39174  static int WinVfs_unlink(const char *zPath)
 39175  {
 39176  	void * pConverted;
 39177  	BOOL rc;
 39178  	pConverted = jx9convertUtf8Filename(zPath);
 39179  	if( pConverted == 0 ){
 39180  		return -1;
 39181  	}
 39182  	rc = DeleteFileW((LPCWSTR)pConverted);
 39183  	HeapFree(GetProcessHeap(), 0, pConverted);
 39184  	return rc ? JX9_OK : - 1;
 39185  }
 39186  /* jx9_int64 (*xFreeSpace)(const char *) */
 39187  static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
 39188  {
 39189  #ifdef _WIN32_WCE
 39190  	/* GetDiskFreeSpace is not supported under WINCE */
 39191  	SXUNUSED(zPath);
 39192  	return 0;
 39193  #else
 39194  	DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
 39195  	void * pConverted;
 39196  	WCHAR *p;
 39197  	BOOL rc;
 39198  	pConverted = jx9convertUtf8Filename(zPath);
 39199  	if( pConverted == 0 ){
 39200  		return 0;
 39201  	}
 39202  	p = (WCHAR *)pConverted;
 39203  	for(;*p;p++){
 39204  		if( *p == '\\' || *p == '/'){
 39205  			*p = '\0';
 39206  			break;
 39207  		}
 39208  	}
 39209  	rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
 39210  	if( !rc ){
 39211  		return 0;
 39212  	}
 39213  	return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
 39214  #endif
 39215  }
 39216  /* jx9_int64 (*xTotalSpace)(const char *) */
 39217  static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
 39218  {
 39219  #ifdef _WIN32_WCE
 39220  	/* GetDiskFreeSpace is not supported under WINCE */
 39221  	SXUNUSED(zPath);
 39222  	return 0;
 39223  #else
 39224  	DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
 39225  	void * pConverted;
 39226  	WCHAR *p;
 39227  	BOOL rc;
 39228  	pConverted = jx9convertUtf8Filename(zPath);
 39229  	if( pConverted == 0 ){
 39230  		return 0;
 39231  	}
 39232  	p = (WCHAR *)pConverted;
 39233  	for(;*p;p++){
 39234  		if( *p == '\\' || *p == '/'){
 39235  			*p = '\0';
 39236  			break;
 39237  		}
 39238  	}
 39239  	rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
 39240  	if( !rc ){
 39241  		return 0;
 39242  	}
 39243  	return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
 39244  #endif
 39245  }
 39246  /* int (*xFileExists)(const char *) */
 39247  static int WinVfs_FileExists(const char *zPath)
 39248  {
 39249  	void * pConverted;
 39250  	DWORD dwAttr;
 39251  	pConverted = jx9convertUtf8Filename(zPath);
 39252  	if( pConverted == 0 ){
 39253  		return -1;
 39254  	}
 39255  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39256  	HeapFree(GetProcessHeap(), 0, pConverted);
 39257  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39258  		return -1;
 39259  	}
 39260  	return JX9_OK;
 39261  }
 39262  /* Open a file in a read-only mode */
 39263  static HANDLE OpenReadOnly(LPCWSTR pPath)
 39264  {
 39265  	DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
 39266  	DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
 39267  	DWORD dwAccess = GENERIC_READ;
 39268  	DWORD dwCreate = OPEN_EXISTING;	
 39269  	HANDLE pHandle;
 39270  	pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
 39271  	if( pHandle == INVALID_HANDLE_VALUE){
 39272  		return 0;
 39273  	}
 39274  	return pHandle;
 39275  }
 39276  /* jx9_int64 (*xFileSize)(const char *) */
 39277  static jx9_int64 WinVfs_FileSize(const char *zPath)
 39278  {
 39279  	DWORD dwLow, dwHigh;
 39280  	void * pConverted;
 39281  	jx9_int64 nSize;
 39282  	HANDLE pHandle;
 39283  	
 39284  	pConverted = jx9convertUtf8Filename(zPath);
 39285  	if( pConverted == 0 ){
 39286  		return -1;
 39287  	}
 39288  	/* Open the file in read-only mode */
 39289  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39290  	HeapFree(GetProcessHeap(), 0, pConverted);
 39291  	if( pHandle ){
 39292  		dwLow = GetFileSize(pHandle, &dwHigh);
 39293  		nSize = dwHigh;
 39294  		nSize <<= 32;
 39295  		nSize += dwLow;
 39296  		CloseHandle(pHandle);
 39297  	}else{
 39298  		nSize = -1;
 39299  	}
 39300  	return nSize;
 39301  }
 39302  #define TICKS_PER_SECOND 10000000
 39303  #define EPOCH_DIFFERENCE 11644473600LL
 39304  /* Convert Windows timestamp to UNIX timestamp */
 39305  static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
 39306  {
 39307      jx9_int64 input, temp;
 39308  	input = pTime->dwHighDateTime;
 39309  	input <<= 32;
 39310  	input += pTime->dwLowDateTime;
 39311      temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
 39312      temp = temp - EPOCH_DIFFERENCE;  /*subtract number of seconds between epochs*/
 39313      return temp;
 39314  }
 39315  /* Convert UNIX timestamp to Windows timestamp */
 39316  static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
 39317  {
 39318    jx9_int64 result = EPOCH_DIFFERENCE;
 39319    result += nUnixtime;
 39320    result *= 10000000LL;
 39321    pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
 39322    pOut->dwLowDateTime = (DWORD)nUnixtime;
 39323  }
 39324  /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
 39325  static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
 39326  {
 39327  	FILETIME sTouch, sAccess;
 39328  	void *pConverted;
 39329  	void *pHandle;
 39330  	BOOL rc = 0;
 39331  	pConverted = jx9convertUtf8Filename(zPath);
 39332  	if( pConverted == 0 ){
 39333  		return -1;
 39334  	}
 39335  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39336  	if( pHandle ){
 39337  		if( touch_time < 0 ){
 39338  			GetSystemTimeAsFileTime(&sTouch);
 39339  		}else{
 39340  			convertUnixTimeToWindowsTime(touch_time, &sTouch);
 39341  		}
 39342  		if( access_time < 0 ){
 39343  			/* Use the touch time */
 39344  			sAccess = sTouch; /* Structure assignment */
 39345  		}else{
 39346  			convertUnixTimeToWindowsTime(access_time, &sAccess);
 39347  		}
 39348  		rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
 39349  		/* Close the handle */
 39350  		CloseHandle(pHandle);
 39351  	}
 39352  	HeapFree(GetProcessHeap(), 0, pConverted);
 39353  	return rc ? JX9_OK : -1;
 39354  }
 39355  /* jx9_int64 (*xFileAtime)(const char *) */
 39356  static jx9_int64 WinVfs_FileAtime(const char *zPath)
 39357  {
 39358  	BY_HANDLE_FILE_INFORMATION sInfo;
 39359  	void * pConverted;
 39360  	jx9_int64 atime;
 39361  	HANDLE pHandle;
 39362  	pConverted = jx9convertUtf8Filename(zPath);
 39363  	if( pConverted == 0 ){
 39364  		return -1;
 39365  	}
 39366  	/* Open the file in read-only mode */
 39367  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39368  	if( pHandle ){
 39369  		BOOL rc;
 39370  		rc = GetFileInformationByHandle(pHandle, &sInfo);
 39371  		if( rc ){
 39372  			atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
 39373  		}else{
 39374  			atime = -1;
 39375  		}
 39376  		CloseHandle(pHandle);
 39377  	}else{
 39378  		atime = -1;
 39379  	}
 39380  	HeapFree(GetProcessHeap(), 0, pConverted);
 39381  	return atime;
 39382  }
 39383  /* jx9_int64 (*xFileMtime)(const char *) */
 39384  static jx9_int64 WinVfs_FileMtime(const char *zPath)
 39385  {
 39386  	BY_HANDLE_FILE_INFORMATION sInfo;
 39387  	void * pConverted;
 39388  	jx9_int64 mtime;
 39389  	HANDLE pHandle;
 39390  	pConverted = jx9convertUtf8Filename(zPath);
 39391  	if( pConverted == 0 ){
 39392  		return -1;
 39393  	}
 39394  	/* Open the file in read-only mode */
 39395  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39396  	if( pHandle ){
 39397  		BOOL rc;
 39398  		rc = GetFileInformationByHandle(pHandle, &sInfo);
 39399  		if( rc ){
 39400  			mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
 39401  		}else{
 39402  			mtime = -1;
 39403  		}
 39404  		CloseHandle(pHandle);
 39405  	}else{
 39406  		mtime = -1;
 39407  	}
 39408  	HeapFree(GetProcessHeap(), 0, pConverted);
 39409  	return mtime;
 39410  }
 39411  /* jx9_int64 (*xFileCtime)(const char *) */
 39412  static jx9_int64 WinVfs_FileCtime(const char *zPath)
 39413  {
 39414  	BY_HANDLE_FILE_INFORMATION sInfo;
 39415  	void * pConverted;
 39416  	jx9_int64 ctime;
 39417  	HANDLE pHandle;
 39418  	pConverted = jx9convertUtf8Filename(zPath);
 39419  	if( pConverted == 0 ){
 39420  		return -1;
 39421  	}
 39422  	/* Open the file in read-only mode */
 39423  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39424  	if( pHandle ){
 39425  		BOOL rc;
 39426  		rc = GetFileInformationByHandle(pHandle, &sInfo);
 39427  		if( rc ){
 39428  			ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
 39429  		}else{
 39430  			ctime = -1;
 39431  		}
 39432  		CloseHandle(pHandle);
 39433  	}else{
 39434  		ctime = -1;
 39435  	}
 39436  	HeapFree(GetProcessHeap(), 0, pConverted);
 39437  	return ctime;
 39438  }
 39439  /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
 39440  /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
 39441  static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
 39442  {
 39443  	BY_HANDLE_FILE_INFORMATION sInfo;
 39444  	void *pConverted;
 39445  	HANDLE pHandle;
 39446  	BOOL rc;
 39447  	pConverted = jx9convertUtf8Filename(zPath);
 39448  	if( pConverted == 0 ){
 39449  		return -1;
 39450  	}
 39451  	/* Open the file in read-only mode */
 39452  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39453  	HeapFree(GetProcessHeap(), 0, pConverted);
 39454  	if( pHandle == 0 ){
 39455  		return -1;
 39456  	}
 39457  	rc = GetFileInformationByHandle(pHandle, &sInfo);
 39458  	CloseHandle(pHandle);
 39459  	if( !rc ){
 39460  		return -1;
 39461  	}
 39462  	/* dev */
 39463  	jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
 39464  	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
 39465  	/* ino */
 39466  	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
 39467  	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
 39468  	/* mode */
 39469  	jx9_value_int(pWorker, 0);
 39470  	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
 39471  	/* nlink */
 39472  	jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
 39473  	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
 39474  	/* uid, gid, rdev */
 39475  	jx9_value_int(pWorker, 0);
 39476  	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
 39477  	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
 39478  	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
 39479  	/* size */
 39480  	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
 39481  	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
 39482  	/* atime */
 39483  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
 39484  	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
 39485  	/* mtime */
 39486  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
 39487  	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
 39488  	/* ctime */
 39489  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
 39490  	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
 39491  	/* blksize, blocks */
 39492  	jx9_value_int(pWorker, 0);		
 39493  	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
 39494  	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
 39495  	return JX9_OK;
 39496  }
 39497  /* int (*xIsfile)(const char *) */
 39498  static int WinVfs_isfile(const char *zPath)
 39499  {
 39500  	void * pConverted;
 39501  	DWORD dwAttr;
 39502  	pConverted = jx9convertUtf8Filename(zPath);
 39503  	if( pConverted == 0 ){
 39504  		return -1;
 39505  	}
 39506  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39507  	HeapFree(GetProcessHeap(), 0, pConverted);
 39508  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39509  		return -1;
 39510  	}
 39511  	return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
 39512  }
 39513  /* int (*xIslink)(const char *) */
 39514  static int WinVfs_islink(const char *zPath)
 39515  {
 39516  	void * pConverted;
 39517  	DWORD dwAttr;
 39518  	pConverted = jx9convertUtf8Filename(zPath);
 39519  	if( pConverted == 0 ){
 39520  		return -1;
 39521  	}
 39522  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39523  	HeapFree(GetProcessHeap(), 0, pConverted);
 39524  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39525  		return -1;
 39526  	}
 39527  	return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
 39528  }
 39529  /* int (*xWritable)(const char *) */
 39530  static int WinVfs_iswritable(const char *zPath)
 39531  {
 39532  	void * pConverted;
 39533  	DWORD dwAttr;
 39534  	pConverted = jx9convertUtf8Filename(zPath);
 39535  	if( pConverted == 0 ){
 39536  		return -1;
 39537  	}
 39538  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39539  	HeapFree(GetProcessHeap(), 0, pConverted);
 39540  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39541  		return -1;
 39542  	}
 39543  	if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
 39544  		/* Not a regular file */
 39545  		return -1;
 39546  	}
 39547  	if( dwAttr & FILE_ATTRIBUTE_READONLY ){
 39548  		/* Read-only file */
 39549  		return -1;
 39550  	}
 39551  	/* File is writable */
 39552  	return JX9_OK;
 39553  }
 39554  /* int (*xExecutable)(const char *) */
 39555  static int WinVfs_isexecutable(const char *zPath)
 39556  {
 39557  	void * pConverted;
 39558  	DWORD dwAttr;
 39559  	pConverted = jx9convertUtf8Filename(zPath);
 39560  	if( pConverted == 0 ){
 39561  		return -1;
 39562  	}
 39563  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39564  	HeapFree(GetProcessHeap(), 0, pConverted);
 39565  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39566  		return -1;
 39567  	}
 39568  	if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
 39569  		/* Not a regular file */
 39570  		return -1;
 39571  	}
 39572  	/* File is executable */
 39573  	return JX9_OK;
 39574  }
 39575  /* int (*xFiletype)(const char *, jx9_context *) */
 39576  static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
 39577  {
 39578  	void * pConverted;
 39579  	DWORD dwAttr;
 39580  	pConverted = jx9convertUtf8Filename(zPath);
 39581  	if( pConverted == 0 ){
 39582  		/* Expand 'unknown' */
 39583  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 39584  		return -1;
 39585  	}
 39586  	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
 39587  	HeapFree(GetProcessHeap(), 0, pConverted);
 39588  	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
 39589  		/* Expand 'unknown' */
 39590  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 39591  		return -1;
 39592  	}
 39593  	if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
 39594  		jx9_result_string(pCtx, "file", sizeof("file")-1);
 39595  	}else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
 39596  		jx9_result_string(pCtx, "dir", sizeof("dir")-1);
 39597  	}else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
 39598  		jx9_result_string(pCtx, "link", sizeof("link")-1);
 39599  	}else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
 39600  		jx9_result_string(pCtx, "block", sizeof("block")-1);
 39601  	}else{
 39602  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 39603  	}
 39604  	return JX9_OK;
 39605  }
 39606  /* int (*xGetenv)(const char *, jx9_context *) */
 39607  static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
 39608  {
 39609  	char zValue[1024];
 39610  	DWORD n;
 39611  	/*
 39612  	 * According to MSDN
 39613  	 * If lpBuffer is not large enough to hold the data, the return 
 39614  	 * value is the buffer size, in characters, required to hold the 
 39615  	 * string and its terminating null character and the contents 
 39616  	 * of lpBuffer are undefined.
 39617  	 */
 39618  	n = sizeof(zValue);
 39619  	SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
 39620  	/* Extract the environment value */
 39621  	n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
 39622  	if( !n ){
 39623  		/* No such variable*/
 39624  		return -1;
 39625  	}
 39626  	jx9_result_string(pCtx, zValue, (int)n);
 39627  	return JX9_OK;
 39628  }
 39629  /* int (*xSetenv)(const char *, const char *) */
 39630  static int WinVfs_Setenv(const char *zName, const char *zValue)
 39631  {
 39632  	BOOL rc;
 39633  	rc = SetEnvironmentVariableA(zName, zValue);
 39634  	return rc ? JX9_OK : -1;
 39635  }
 39636  /* int (*xMmap)(const char *, void **, jx9_int64 *) */
 39637  static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
 39638  {
 39639  	DWORD dwSizeLow, dwSizeHigh;
 39640  	HANDLE pHandle, pMapHandle;
 39641  	void *pConverted, *pView;
 39642  
 39643  	pConverted = jx9convertUtf8Filename(zPath);
 39644  	if( pConverted == 0 ){
 39645  		return -1;
 39646  	}
 39647  	pHandle = OpenReadOnly((LPCWSTR)pConverted);
 39648  	HeapFree(GetProcessHeap(), 0, pConverted);
 39649  	if( pHandle == 0 ){
 39650  		return -1;
 39651  	}
 39652  	/* Get the file size */
 39653  	dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
 39654  	/* Create the mapping */
 39655  	pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
 39656  	if( pMapHandle == 0 ){
 39657  		CloseHandle(pHandle);
 39658  		return -1;
 39659  	}
 39660  	*pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
 39661  	/* Obtain the view */
 39662  	pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
 39663  	if( pView ){
 39664  		/* Let the upper layer point to the view */
 39665  		*ppMap = pView;
 39666  	}
 39667  	/* Close the handle
 39668  	 * According to MSDN it's OK the close the HANDLES.
 39669  	 */
 39670  	CloseHandle(pMapHandle);
 39671  	CloseHandle(pHandle);
 39672  	return pView ? JX9_OK : -1;
 39673  }
 39674  /* void (*xUnmap)(void *, jx9_int64)  */
 39675  static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
 39676  {
 39677  	nSize = 0; /* Compiler warning */
 39678  	UnmapViewOfFile(pView);
 39679  }
 39680  /* void (*xTempDir)(jx9_context *) */
 39681  static void WinVfs_TempDir(jx9_context *pCtx)
 39682  {
 39683  	CHAR zTemp[1024];
 39684  	DWORD n;
 39685  	n = GetTempPathA(sizeof(zTemp), zTemp);
 39686  	if( n < 1 ){
 39687  		/* Assume the default windows temp directory */
 39688  		jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
 39689  	}else{
 39690  		jx9_result_string(pCtx, zTemp, (int)n);
 39691  	}
 39692  }
 39693  /* unsigned int (*xProcessId)(void) */
 39694  static unsigned int WinVfs_ProcessId(void)
 39695  {
 39696  	DWORD nID = 0;
 39697  #ifndef __MINGW32__
 39698  	nID = GetProcessId(GetCurrentProcess());
 39699  #endif /* __MINGW32__ */
 39700  	return (unsigned int)nID;
 39701  }
 39702  
 39703  /* Export the windows vfs */
 39704  static const jx9_vfs sWinVfs = {
 39705  	"Windows_vfs", 
 39706  	JX9_VFS_VERSION, 
 39707  	WinVfs_chdir,    /* int (*xChdir)(const char *) */
 39708  	0,               /* int (*xChroot)(const char *); */
 39709  	WinVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
 39710  	WinVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
 39711  	WinVfs_rmdir,    /* int (*xRmdir)(const char *) */ 
 39712  	WinVfs_isdir,    /* int (*xIsdir)(const char *) */
 39713  	WinVfs_Rename,   /* int (*xRename)(const char *, const char *) */
 39714  	WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
 39715  	WinVfs_Sleep,               /* int (*xSleep)(unsigned int) */
 39716  	WinVfs_unlink,   /* int (*xUnlink)(const char *) */
 39717  	WinVfs_FileExists, /* int (*xFileExists)(const char *) */
 39718  	0, /*int (*xChmod)(const char *, int)*/
 39719  	0, /*int (*xChown)(const char *, const char *)*/
 39720  	0, /*int (*xChgrp)(const char *, const char *)*/
 39721  	WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
 39722  	WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
 39723  	WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
 39724  	WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
 39725  	WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
 39726  	WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
 39727  	WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
 39728  	WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
 39729  	WinVfs_isfile,     /* int (*xIsfile)(const char *) */
 39730  	WinVfs_islink,     /* int (*xIslink)(const char *) */
 39731  	WinVfs_isfile,     /* int (*xReadable)(const char *) */
 39732  	WinVfs_iswritable, /* int (*xWritable)(const char *) */
 39733  	WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
 39734  	WinVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
 39735  	WinVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
 39736  	WinVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */ 
 39737  	WinVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
 39738  	WinVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
 39739  	WinVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
 39740  	0,                 /* int (*xLink)(const char *, const char *, int) */
 39741  	0,                 /* int (*xUmask)(int) */
 39742  	WinVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
 39743  	WinVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
 39744  	0, /* int (*xUid)(void) */
 39745  	0, /* int (*xGid)(void) */
 39746  	0, /* void (*xUsername)(jx9_context *) */
 39747  	0  /* int (*xExec)(const char *, jx9_context *) */
 39748  };
 39749  /* Windows file IO */
 39750  #ifndef INVALID_SET_FILE_POINTER
 39751  # define INVALID_SET_FILE_POINTER ((DWORD)-1)
 39752  #endif
 39753  /* int (*xOpen)(const char *, int, jx9_value *, void **) */
 39754  static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
 39755  {
 39756  	DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
 39757  	DWORD dwAccess = GENERIC_READ;
 39758  	DWORD dwShare, dwCreate;	
 39759  	void *pConverted;
 39760  	HANDLE pHandle;
 39761  
 39762  	pConverted = jx9convertUtf8Filename(zPath);
 39763  	if( pConverted == 0 ){
 39764  		return -1;
 39765  	}
 39766  	/* Set the desired flags according to the open mode */
 39767  	if( iOpenMode & JX9_IO_OPEN_CREATE ){
 39768  		/* Open existing file, or create if it doesn't exist */
 39769  		dwCreate = OPEN_ALWAYS;
 39770  		if( iOpenMode & JX9_IO_OPEN_TRUNC ){
 39771  			/* If the specified file exists and is writable, the function overwrites the file */
 39772  			dwCreate = CREATE_ALWAYS;
 39773  		}
 39774  	}else if( iOpenMode & JX9_IO_OPEN_EXCL ){
 39775  		/* Creates a new file, only if it does not already exist.
 39776  		* If the file exists, it fails.
 39777  		*/
 39778  		dwCreate = CREATE_NEW;
 39779  	}else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
 39780  		/* Opens a file and truncates it so that its size is zero bytes
 39781  		 * The file must exist.
 39782  		 */
 39783  		dwCreate = TRUNCATE_EXISTING;
 39784  	}else{
 39785  		/* Opens a file, only if it exists. */
 39786  		dwCreate = OPEN_EXISTING;
 39787  	}
 39788  	if( iOpenMode & JX9_IO_OPEN_RDWR ){
 39789  		/* Read+Write access */
 39790  		dwAccess |= GENERIC_WRITE;
 39791  	}else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
 39792  		/* Write only access */
 39793  		dwAccess = GENERIC_WRITE;
 39794  	}
 39795  	if( iOpenMode & JX9_IO_OPEN_APPEND ){
 39796  		/* Append mode */
 39797  		dwAccess = FILE_APPEND_DATA;
 39798  	}
 39799  	if( iOpenMode & JX9_IO_OPEN_TEMP ){
 39800  		/* File is temporary */
 39801  		dwType = FILE_ATTRIBUTE_TEMPORARY;
 39802  	}
 39803  	dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
 39804  	pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
 39805  	HeapFree(GetProcessHeap(), 0, pConverted);
 39806  	if( pHandle == INVALID_HANDLE_VALUE){
 39807  		SXUNUSED(pResource); /* MSVC warning */
 39808  		return -1;
 39809  	}
 39810  	/* Make the handle accessible to the upper layer */
 39811  	*ppHandle = (void *)pHandle;
 39812  	return JX9_OK;
 39813  }
 39814  /* An instance of the following structure is used to record state information 
 39815   * while iterating throw directory entries.
 39816   */
 39817  typedef struct WinDir_Info WinDir_Info;
 39818  struct WinDir_Info
 39819  {
 39820  	HANDLE pDirHandle;
 39821  	void *pPath;
 39822  	WIN32_FIND_DATAW sInfo;
 39823  	int rc;
 39824  };
 39825  /* int (*xOpenDir)(const char *, jx9_value *, void **) */
 39826  static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
 39827  {
 39828  	WinDir_Info *pDirInfo;
 39829  	void *pConverted;
 39830  	char *zPrep;
 39831  	sxu32 n;
 39832  	/* Prepare the path */
 39833  	n = SyStrlen(zPath);
 39834  	zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
 39835  	if( zPrep == 0 ){
 39836  		return -1;
 39837  	}
 39838  	SyMemcpy((const void *)zPath, zPrep, n);
 39839  	zPrep[n]   = '\\';
 39840  	zPrep[n+1] =  '*';
 39841  	zPrep[n+2] = 0;
 39842  	pConverted = jx9convertUtf8Filename(zPrep);
 39843  	HeapFree(GetProcessHeap(), 0, zPrep);
 39844  	if( pConverted == 0 ){
 39845  		return -1;
 39846  	}
 39847  	/* Allocate a new instance */
 39848  	pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
 39849  	if( pDirInfo == 0 ){
 39850  		pResource = 0; /* Compiler warning */
 39851  		return -1;
 39852  	}
 39853  	pDirInfo->rc = SXRET_OK;
 39854  	pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
 39855  	if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
 39856  		/* Cannot open directory */
 39857  		HeapFree(GetProcessHeap(), 0, pConverted);
 39858  		HeapFree(GetProcessHeap(), 0, pDirInfo);
 39859  		return -1;
 39860  	}
 39861  	/* Save the path */
 39862  	pDirInfo->pPath = pConverted;
 39863  	/* Save our structure */
 39864  	*ppHandle = pDirInfo;
 39865  	return JX9_OK;
 39866  }
 39867  /* void (*xCloseDir)(void *) */
 39868  static void WinDir_Close(void *pUserData)
 39869  {
 39870  	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
 39871  	if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
 39872  		FindClose(pDirInfo->pDirHandle);
 39873  	}
 39874  	HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
 39875  	HeapFree(GetProcessHeap(), 0, pDirInfo);
 39876  }
 39877  /* void (*xClose)(void *); */
 39878  static void WinFile_Close(void *pUserData)
 39879  {
 39880  	HANDLE pHandle = (HANDLE)pUserData;
 39881  	CloseHandle(pHandle);
 39882  }
 39883  /* int (*xReadDir)(void *, jx9_context *) */
 39884  static int WinDir_Read(void *pUserData, jx9_context *pCtx)
 39885  {
 39886  	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
 39887  	LPWIN32_FIND_DATAW pData;
 39888  	char *zName;
 39889  	BOOL rc;
 39890  	sxu32 n;
 39891  	if( pDirInfo->rc != SXRET_OK ){
 39892  		/* No more entry to process */
 39893  		return -1;
 39894  	}
 39895  	pData = &pDirInfo->sInfo;
 39896  	for(;;){
 39897  		zName = jx9unicodeToUtf8(pData->cFileName);
 39898  		if( zName == 0 ){
 39899  			/* Out of memory */
 39900  			return -1;
 39901  		}
 39902  		n = SyStrlen(zName);
 39903  		/* Ignore '.' && '..' */
 39904  		if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
 39905  			break;
 39906  		}
 39907  		HeapFree(GetProcessHeap(), 0, zName);
 39908  		rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
 39909  		if( !rc ){
 39910  			return -1;
 39911  		}
 39912  	}
 39913  	/* Return the current file name */
 39914  	jx9_result_string(pCtx, zName, -1);
 39915  	HeapFree(GetProcessHeap(), 0, zName);
 39916  	/* Point to the next entry */
 39917  	rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
 39918  	if( !rc ){
 39919  		pDirInfo->rc = SXERR_EOF;
 39920  	}
 39921  	return JX9_OK;
 39922  }
 39923  /* void (*xRewindDir)(void *) */
 39924  static void WinDir_RewindDir(void *pUserData)
 39925  {
 39926  	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
 39927  	FindClose(pDirInfo->pDirHandle);
 39928  	pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
 39929  	if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
 39930  		pDirInfo->rc = SXERR_EOF;
 39931  	}else{
 39932  		pDirInfo->rc = SXRET_OK;
 39933  	}
 39934  }
 39935  /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
 39936  static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
 39937  {
 39938  	HANDLE pHandle = (HANDLE)pOS;
 39939  	DWORD nRd;
 39940  	BOOL rc;
 39941  	rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
 39942  	if( !rc ){
 39943  		/* EOF or IO error */
 39944  		return -1;
 39945  	}
 39946  	return (jx9_int64)nRd;
 39947  }
 39948  /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
 39949  static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
 39950  {
 39951  	const char *zData = (const char *)pBuffer;
 39952  	HANDLE pHandle = (HANDLE)pOS;
 39953  	jx9_int64 nCount;
 39954  	DWORD nWr;
 39955  	BOOL rc;
 39956  	nWr = 0;
 39957  	nCount = 0;
 39958  	for(;;){
 39959  		if( nWrite < 1 ){
 39960  			break;
 39961  		}
 39962  		rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
 39963  		if( !rc ){
 39964  			/* IO error */
 39965  			break;
 39966  		}
 39967  		nWrite -= nWr;
 39968  		nCount += nWr;
 39969  		zData += nWr;
 39970  	}
 39971  	if( nWrite > 0 ){
 39972  		return -1;
 39973  	}
 39974  	return nCount;
 39975  }
 39976  /* int (*xSeek)(void *, jx9_int64, int) */
 39977  static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
 39978  {
 39979  	HANDLE pHandle = (HANDLE)pUserData;
 39980  	DWORD dwMove, dwNew;
 39981  	LONG nHighOfft;
 39982  	switch(whence){
 39983  	case 1:/*SEEK_CUR*/
 39984  		dwMove = FILE_CURRENT;
 39985  		break;
 39986  	case 2: /* SEEK_END */
 39987  		dwMove = FILE_END;
 39988  		break;
 39989  	case 0: /* SEEK_SET */
 39990  	default:
 39991  		dwMove = FILE_BEGIN;
 39992  		break;
 39993  	}
 39994  	nHighOfft = (LONG)(iOfft >> 32);
 39995  	dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
 39996  	if( dwNew == INVALID_SET_FILE_POINTER ){
 39997  		return -1;
 39998  	}
 39999  	return JX9_OK;
 40000  }
 40001  /* int (*xLock)(void *, int) */
 40002  static int WinFile_Lock(void *pUserData, int lock_type)
 40003  {
 40004  	HANDLE pHandle = (HANDLE)pUserData;
 40005  	static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
 40006  	OVERLAPPED sDummy;
 40007  	BOOL rc;
 40008  	SyZero(&sDummy, sizeof(sDummy));
 40009  	/* Get the file size */
 40010  	if( lock_type < 1 ){
 40011  		/* Unlock the file */
 40012  		rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
 40013  	}else{
 40014  		DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
 40015  		/* Lock the file */
 40016  		if( lock_type == 1 /* LOCK_EXCL */ ){
 40017  			dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
 40018  		}
 40019  		dwLo = GetFileSize(pHandle, &dwHi);
 40020  		rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
 40021  	}
 40022  	return rc ? JX9_OK : -1 /* Lock error */;
 40023  }
 40024  /* jx9_int64 (*xTell)(void *) */
 40025  static jx9_int64 WinFile_Tell(void *pUserData)
 40026  {
 40027  	HANDLE pHandle = (HANDLE)pUserData;
 40028  	DWORD dwNew;
 40029  	dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
 40030  	if( dwNew == INVALID_SET_FILE_POINTER ){
 40031  		return -1;
 40032  	}
 40033  	return (jx9_int64)dwNew;
 40034  }
 40035  /* int (*xTrunc)(void *, jx9_int64) */
 40036  static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
 40037  {
 40038  	HANDLE pHandle = (HANDLE)pUserData;
 40039  	LONG HighOfft;
 40040  	DWORD dwNew;
 40041  	BOOL rc;
 40042  	HighOfft = (LONG)(nOfft >> 32);
 40043  	dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
 40044  	if( dwNew == INVALID_SET_FILE_POINTER ){
 40045  		return -1;
 40046  	}
 40047  	rc = SetEndOfFile(pHandle);
 40048  	return rc ? JX9_OK : -1;
 40049  }
 40050  /* int (*xSync)(void *); */
 40051  static int WinFile_Sync(void *pUserData)
 40052  {
 40053  	HANDLE pHandle = (HANDLE)pUserData;
 40054  	BOOL rc;
 40055  	rc = FlushFileBuffers(pHandle);
 40056  	return rc ? JX9_OK : - 1;
 40057  }
 40058  /* int (*xStat)(void *, jx9_value *, jx9_value *) */
 40059  static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
 40060  {
 40061  	BY_HANDLE_FILE_INFORMATION sInfo;
 40062  	HANDLE pHandle = (HANDLE)pUserData;
 40063  	BOOL rc;
 40064  	rc = GetFileInformationByHandle(pHandle, &sInfo);
 40065  	if( !rc ){
 40066  		return -1;
 40067  	}
 40068  	/* dev */
 40069  	jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
 40070  	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
 40071  	/* ino */
 40072  	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
 40073  	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
 40074  	/* mode */
 40075  	jx9_value_int(pWorker, 0);
 40076  	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
 40077  	/* nlink */
 40078  	jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
 40079  	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
 40080  	/* uid, gid, rdev */
 40081  	jx9_value_int(pWorker, 0);
 40082  	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
 40083  	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
 40084  	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
 40085  	/* size */
 40086  	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
 40087  	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
 40088  	/* atime */
 40089  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
 40090  	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
 40091  	/* mtime */
 40092  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
 40093  	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
 40094  	/* ctime */
 40095  	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
 40096  	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
 40097  	/* blksize, blocks */
 40098  	jx9_value_int(pWorker, 0);		
 40099  	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
 40100  	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
 40101  	return JX9_OK;
 40102  }
 40103  /* Export the file:// stream */
 40104  static const jx9_io_stream sWinFileStream = {
 40105  	"file", /* Stream name */
 40106  	JX9_IO_STREAM_VERSION, 
 40107  	WinFile_Open,  /* xOpen */
 40108  	WinDir_Open,   /* xOpenDir */
 40109  	WinFile_Close, /* xClose */
 40110  	WinDir_Close,  /* xCloseDir */
 40111  	WinFile_Read,  /* xRead */
 40112  	WinDir_Read,   /* xReadDir */
 40113  	WinFile_Write, /* xWrite */
 40114  	WinFile_Seek,  /* xSeek */
 40115  	WinFile_Lock,  /* xLock */
 40116  	WinDir_RewindDir, /* xRewindDir */
 40117  	WinFile_Tell,  /* xTell */
 40118  	WinFile_Trunc, /* xTrunc */
 40119  	WinFile_Sync,  /* xSeek */
 40120  	WinFile_Stat   /* xStat */
 40121  };
 40122  #elif defined(__UNIXES__)
 40123  /*
 40124   * UNIX VFS implementation for the JX9 engine.
 40125   * Authors:
 40126   *    Symisc Systems, devel@symisc.net.
 40127   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 40128   * Status:
 40129   *    Stable.
 40130   */
 40131  #include <sys/types.h>
 40132  #include <limits.h>
 40133  #include <fcntl.h>
 40134  #include <unistd.h>
 40135  #include <sys/uio.h>
 40136  #include <sys/stat.h>
 40137  #include <sys/mman.h>
 40138  #include <sys/file.h>
 40139  #include <pwd.h>
 40140  #include <grp.h>
 40141  #include <dirent.h>
 40142  #include <utime.h>
 40143  #include <stdio.h>
 40144  #include <stdlib.h>
 40145  /* int (*xchdir)(const char *) */
 40146  static int UnixVfs_chdir(const char *zPath)
 40147  {
 40148    int rc;
 40149    rc = chdir(zPath);
 40150    return rc == 0 ? JX9_OK : -1;
 40151  }
 40152  /* int (*xGetcwd)(jx9_context *) */
 40153  static int UnixVfs_getcwd(jx9_context *pCtx)
 40154  {
 40155  	char zBuf[4096];
 40156  	char *zDir;
 40157  	/* Get the current directory */
 40158  	zDir = getcwd(zBuf, sizeof(zBuf));
 40159  	if( zDir == 0 ){
 40160  	  return -1;
 40161      }
 40162  	jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
 40163  	return JX9_OK;
 40164  }
 40165  /* int (*xMkdir)(const char *, int, int) */
 40166  static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
 40167  {
 40168  	int rc;
 40169          rc = mkdir(zPath, mode);
 40170  	recursive = 0; /* cc warning */
 40171  	return rc == 0 ? JX9_OK : -1;
 40172  }
 40173  /* int (*xRmdir)(const char *) */
 40174  static int UnixVfs_rmdir(const char *zPath)
 40175  {
 40176  	int rc;
 40177  	rc = rmdir(zPath);
 40178  	return rc == 0 ? JX9_OK : -1;
 40179  }
 40180  /* int (*xIsdir)(const char *) */
 40181  static int UnixVfs_isdir(const char *zPath)
 40182  {
 40183  	struct stat st;
 40184  	int rc;
 40185  	rc = stat(zPath, &st);
 40186  	if( rc != 0 ){
 40187  	 return -1;
 40188  	}
 40189  	rc = S_ISDIR(st.st_mode);
 40190  	return rc ? JX9_OK : -1 ;
 40191  }
 40192  /* int (*xRename)(const char *, const char *) */
 40193  static int UnixVfs_Rename(const char *zOld, const char *zNew)
 40194  {
 40195  	int rc;
 40196  	rc = rename(zOld, zNew);
 40197  	return rc == 0 ? JX9_OK : -1;
 40198  }
 40199  /* int (*xRealpath)(const char *, jx9_context *) */
 40200  static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
 40201  {
 40202  #ifndef JX9_UNIX_OLD_LIBC
 40203  	char *zReal;
 40204  	zReal = realpath(zPath, 0);
 40205  	if( zReal == 0 ){
 40206  	  return -1;
 40207  	}
 40208  	jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
 40209          /* Release the allocated buffer */
 40210  	free(zReal);
 40211  	return JX9_OK;
 40212  #else
 40213      zPath = 0; /* cc warning */
 40214      pCtx = 0;
 40215      return -1;
 40216  #endif
 40217  }
 40218  /* int (*xSleep)(unsigned int) */
 40219  static int UnixVfs_Sleep(unsigned int uSec)
 40220  {
 40221  	usleep(uSec);
 40222  	return JX9_OK;
 40223  }
 40224  /* int (*xUnlink)(const char *) */
 40225  static int UnixVfs_unlink(const char *zPath)
 40226  {
 40227  	int rc;
 40228  	rc = unlink(zPath);
 40229  	return rc == 0 ? JX9_OK : -1 ;
 40230  }
 40231  /* int (*xFileExists)(const char *) */
 40232  static int UnixVfs_FileExists(const char *zPath)
 40233  {
 40234  	int rc;
 40235  	rc = access(zPath, F_OK);
 40236  	return rc == 0 ? JX9_OK : -1;
 40237  }
 40238  /* jx9_int64 (*xFileSize)(const char *) */
 40239  static jx9_int64 UnixVfs_FileSize(const char *zPath)
 40240  {
 40241  	struct stat st;
 40242  	int rc;
 40243  	rc = stat(zPath, &st);
 40244  	if( rc != 0 ){ 
 40245  	 return -1;
 40246  	}
 40247  	return (jx9_int64)st.st_size;
 40248  }
 40249  /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
 40250  static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
 40251  {
 40252  	struct utimbuf ut;
 40253  	int rc;
 40254  	ut.actime  = (time_t)access_time;
 40255  	ut.modtime = (time_t)touch_time;
 40256  	rc = utime(zPath, &ut);
 40257  	if( rc != 0 ){
 40258  	 return -1;
 40259  	}
 40260  	return JX9_OK;
 40261  }
 40262  /* jx9_int64 (*xFileAtime)(const char *) */
 40263  static jx9_int64 UnixVfs_FileAtime(const char *zPath)
 40264  {
 40265  	struct stat st;
 40266  	int rc;
 40267  	rc = stat(zPath, &st);
 40268  	if( rc != 0 ){ 
 40269  	 return -1;
 40270  	}
 40271  	return (jx9_int64)st.st_atime;
 40272  }
 40273  /* jx9_int64 (*xFileMtime)(const char *) */
 40274  static jx9_int64 UnixVfs_FileMtime(const char *zPath)
 40275  {
 40276  	struct stat st;
 40277  	int rc;
 40278  	rc = stat(zPath, &st);
 40279  	if( rc != 0 ){ 
 40280  	 return -1;
 40281  	}
 40282  	return (jx9_int64)st.st_mtime;
 40283  }
 40284  /* jx9_int64 (*xFileCtime)(const char *) */
 40285  static jx9_int64 UnixVfs_FileCtime(const char *zPath)
 40286  {
 40287  	struct stat st;
 40288  	int rc;
 40289  	rc = stat(zPath, &st);
 40290  	if( rc != 0 ){ 
 40291  	 return -1;
 40292  	}
 40293  	return (jx9_int64)st.st_ctime;
 40294  }
 40295  /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
 40296  static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
 40297  {
 40298  	struct stat st;
 40299  	int rc;
 40300  	rc = stat(zPath, &st);
 40301  	if( rc != 0 ){ 
 40302  	 return -1;
 40303  	}
 40304  	/* dev */
 40305  	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
 40306  	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
 40307  	/* ino */
 40308  	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
 40309  	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
 40310  	/* mode */
 40311  	jx9_value_int(pWorker, (int)st.st_mode);
 40312  	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
 40313  	/* nlink */
 40314  	jx9_value_int(pWorker, (int)st.st_nlink);
 40315  	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
 40316  	/* uid, gid, rdev */
 40317  	jx9_value_int(pWorker, (int)st.st_uid);
 40318  	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
 40319  	jx9_value_int(pWorker, (int)st.st_gid);
 40320  	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
 40321  	jx9_value_int(pWorker, (int)st.st_rdev);
 40322  	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
 40323  	/* size */
 40324  	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
 40325  	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
 40326  	/* atime */
 40327  	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
 40328  	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
 40329  	/* mtime */
 40330  	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
 40331  	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
 40332  	/* ctime */
 40333  	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
 40334  	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
 40335  	/* blksize, blocks */
 40336  	jx9_value_int(pWorker, (int)st.st_blksize);		
 40337  	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
 40338  	jx9_value_int(pWorker, (int)st.st_blocks);
 40339  	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
 40340  	return JX9_OK;
 40341  }
 40342  /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
 40343  static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
 40344  {
 40345  	struct stat st;
 40346  	int rc;
 40347  	rc = lstat(zPath, &st);
 40348  	if( rc != 0 ){ 
 40349  	 return -1;
 40350  	}
 40351  	/* dev */
 40352  	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
 40353  	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
 40354  	/* ino */
 40355  	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
 40356  	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
 40357  	/* mode */
 40358  	jx9_value_int(pWorker, (int)st.st_mode);
 40359  	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
 40360  	/* nlink */
 40361  	jx9_value_int(pWorker, (int)st.st_nlink);
 40362  	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
 40363  	/* uid, gid, rdev */
 40364  	jx9_value_int(pWorker, (int)st.st_uid);
 40365  	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
 40366  	jx9_value_int(pWorker, (int)st.st_gid);
 40367  	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
 40368  	jx9_value_int(pWorker, (int)st.st_rdev);
 40369  	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
 40370  	/* size */
 40371  	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
 40372  	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
 40373  	/* atime */
 40374  	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
 40375  	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
 40376  	/* mtime */
 40377  	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
 40378  	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
 40379  	/* ctime */
 40380  	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
 40381  	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
 40382  	/* blksize, blocks */
 40383  	jx9_value_int(pWorker, (int)st.st_blksize);		
 40384  	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
 40385  	jx9_value_int(pWorker, (int)st.st_blocks);
 40386  	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
 40387  	return JX9_OK;
 40388  }
 40389  /* int (*xChmod)(const char *, int) */
 40390  static int UnixVfs_Chmod(const char *zPath, int mode)
 40391  {
 40392      int rc;
 40393      rc = chmod(zPath, (mode_t)mode);
 40394      return rc == 0 ? JX9_OK : - 1;
 40395  }
 40396  /* int (*xChown)(const char *, const char *) */
 40397  static int UnixVfs_Chown(const char *zPath, const char *zUser)
 40398  {
 40399  #ifndef JX9_UNIX_STATIC_BUILD
 40400    struct passwd *pwd;
 40401    uid_t uid;
 40402    int rc;
 40403    pwd = getpwnam(zUser);   /* Try getting UID for username */
 40404    if (pwd == 0) {
 40405      return -1;
 40406    }
 40407    uid = pwd->pw_uid;
 40408    rc = chown(zPath, uid, -1);
 40409    return rc == 0 ? JX9_OK : -1;
 40410  #else
 40411  	SXUNUSED(zPath);
 40412  	SXUNUSED(zUser);
 40413  	return -1;
 40414  #endif /* JX9_UNIX_STATIC_BUILD */
 40415  }
 40416  /* int (*xChgrp)(const char *, const char *) */
 40417  static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
 40418  {
 40419  #ifndef JX9_UNIX_STATIC_BUILD
 40420    struct group *group;
 40421    gid_t gid;
 40422    int rc;
 40423    group = getgrnam(zGroup);
 40424    if (group == 0) {
 40425      return -1;
 40426    }
 40427    gid = group->gr_gid;
 40428    rc = chown(zPath, -1, gid);
 40429    return rc == 0 ? JX9_OK : -1;
 40430  #else
 40431  	SXUNUSED(zPath);
 40432  	SXUNUSED(zGroup);
 40433  	return -1;
 40434  #endif /* JX9_UNIX_STATIC_BUILD */
 40435  }
 40436  /* int (*xIsfile)(const char *) */
 40437  static int UnixVfs_isfile(const char *zPath)
 40438  {
 40439  	struct stat st;
 40440  	int rc;
 40441  	rc = stat(zPath, &st);
 40442  	if( rc != 0 ){
 40443  	 return -1;
 40444  	}
 40445  	rc = S_ISREG(st.st_mode);
 40446  	return rc ? JX9_OK : -1 ;
 40447  }
 40448  /* int (*xIslink)(const char *) */
 40449  static int UnixVfs_islink(const char *zPath)
 40450  {
 40451  	struct stat st;
 40452  	int rc;
 40453  	rc = stat(zPath, &st);
 40454  	if( rc != 0 ){
 40455  	 return -1;
 40456  	}
 40457  	rc = S_ISLNK(st.st_mode);
 40458  	return rc ? JX9_OK : -1 ;
 40459  }
 40460  /* int (*xReadable)(const char *) */
 40461  static int UnixVfs_isreadable(const char *zPath)
 40462  {
 40463  	int rc;
 40464  	rc = access(zPath, R_OK);
 40465  	return rc == 0 ? JX9_OK : -1;
 40466  }
 40467  /* int (*xWritable)(const char *) */
 40468  static int UnixVfs_iswritable(const char *zPath)
 40469  {
 40470  	int rc;
 40471  	rc = access(zPath, W_OK);
 40472  	return rc == 0 ? JX9_OK : -1;
 40473  }
 40474  /* int (*xExecutable)(const char *) */
 40475  static int UnixVfs_isexecutable(const char *zPath)
 40476  {
 40477  	int rc;
 40478  	rc = access(zPath, X_OK);
 40479  	return rc == 0 ? JX9_OK : -1;
 40480  }
 40481  /* int (*xFiletype)(const char *, jx9_context *) */
 40482  static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
 40483  {
 40484  	struct stat st;
 40485  	int rc;
 40486      rc = stat(zPath, &st);
 40487  	if( rc != 0 ){
 40488  	  /* Expand 'unknown' */
 40489  	  jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 40490  	  return -1;
 40491  	}
 40492  	if(S_ISREG(st.st_mode) ){
 40493  		jx9_result_string(pCtx, "file", sizeof("file")-1);
 40494  	}else if(S_ISDIR(st.st_mode)){
 40495  		jx9_result_string(pCtx, "dir", sizeof("dir")-1);
 40496  	}else if(S_ISLNK(st.st_mode)){
 40497  		jx9_result_string(pCtx, "link", sizeof("link")-1);
 40498  	}else if(S_ISBLK(st.st_mode)){
 40499  		jx9_result_string(pCtx, "block", sizeof("block")-1);
 40500      }else if(S_ISSOCK(st.st_mode)){
 40501  		jx9_result_string(pCtx, "socket", sizeof("socket")-1);
 40502  	}else if(S_ISFIFO(st.st_mode)){
 40503         jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
 40504  	}else{
 40505  		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
 40506  	}
 40507  	return JX9_OK;
 40508  }
 40509  /* int (*xGetenv)(const char *, jx9_context *) */
 40510  static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
 40511  {
 40512  	char *zEnv;
 40513  	zEnv = getenv(zVar);
 40514  	if( zEnv == 0 ){
 40515  	  return -1;
 40516  	}
 40517  	jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
 40518  	return JX9_OK;
 40519  }
 40520  /* int (*xSetenv)(const char *, const char *) */
 40521  static int UnixVfs_Setenv(const char *zName, const char *zValue)
 40522  {
 40523     int rc;
 40524     rc = setenv(zName, zValue, 1);
 40525     return rc == 0 ? JX9_OK : -1;
 40526  }
 40527  /* int (*xMmap)(const char *, void **, jx9_int64 *) */
 40528  static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
 40529  {
 40530  	struct stat st;
 40531  	void *pMap;
 40532  	int fd;
 40533  	int rc;
 40534  	/* Open the file in a read-only mode */
 40535  	fd = open(zPath, O_RDONLY);
 40536  	if( fd < 0 ){
 40537  		return -1;
 40538  	}
 40539  	/* stat the handle */
 40540  	fstat(fd, &st);
 40541  	/* Obtain a memory view of the whole file */
 40542  	pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
 40543  	rc = JX9_OK;
 40544  	if( pMap == MAP_FAILED ){
 40545  		rc = -1;
 40546  	}else{
 40547  		/* Point to the memory view */
 40548  		*ppMap = pMap;
 40549  		*pSize = (jx9_int64)st.st_size;
 40550  	}
 40551  	close(fd);
 40552  	return rc;
 40553  }
 40554  /* void (*xUnmap)(void *, jx9_int64)  */
 40555  static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
 40556  {
 40557  	munmap(pView, (size_t)nSize);
 40558  }
 40559  /* void (*xTempDir)(jx9_context *) */
 40560  static void UnixVfs_TempDir(jx9_context *pCtx)
 40561  {
 40562  	static const char *azDirs[] = {
 40563       "/var/tmp", 
 40564       "/usr/tmp", 
 40565  	 "/usr/local/tmp"
 40566    };
 40567    unsigned int i;
 40568    struct stat buf;
 40569    const char *zDir;
 40570    zDir = getenv("TMPDIR");
 40571    if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
 40572  	  jx9_result_string(pCtx, zDir, -1);
 40573  	  return;
 40574    }
 40575    for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
 40576  	zDir=azDirs[i];
 40577      if( zDir==0 ) continue;
 40578      if( stat(zDir, &buf) ) continue;
 40579      if( !S_ISDIR(buf.st_mode) ) continue;
 40580      if( access(zDir, 07) ) continue;
 40581      /* Got one */
 40582  	jx9_result_string(pCtx, zDir, -1);
 40583  	return;
 40584    }
 40585    /* Default temp dir */
 40586    jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
 40587  }
 40588  /* unsigned int (*xProcessId)(void) */
 40589  static unsigned int UnixVfs_ProcessId(void)
 40590  {
 40591  	return (unsigned int)getpid();
 40592  }
 40593  /* int (*xUid)(void) */
 40594  static int UnixVfs_uid(void)
 40595  {
 40596  	return (int)getuid();
 40597  }
 40598  /* int (*xGid)(void) */
 40599  static int UnixVfs_gid(void)
 40600  {
 40601  	return (int)getgid();
 40602  }
 40603  /* int (*xUmask)(int) */
 40604  static int UnixVfs_Umask(int new_mask)
 40605  {
 40606  	int old_mask;
 40607  	old_mask = umask(new_mask);
 40608  	return old_mask;
 40609  }
 40610  /* void (*xUsername)(jx9_context *) */
 40611  static void UnixVfs_Username(jx9_context *pCtx)
 40612  {
 40613  #ifndef JX9_UNIX_STATIC_BUILD
 40614    struct passwd *pwd;
 40615    uid_t uid;
 40616    uid = getuid();
 40617    pwd = getpwuid(uid);   /* Try getting UID for username */
 40618    if (pwd == 0) {
 40619      return;
 40620    }
 40621    /* Return the username */
 40622    jx9_result_string(pCtx, pwd->pw_name, -1);
 40623  #else
 40624    jx9_result_string(pCtx, "Unknown", -1);
 40625  #endif /* JX9_UNIX_STATIC_BUILD */
 40626    return;
 40627  }
 40628  /* int (*xLink)(const char *, const char *, int) */
 40629  static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
 40630  {
 40631  	int rc;
 40632  	if( is_sym ){
 40633  		/* Symbolic link */
 40634  		rc = symlink(zSrc, zTarget);
 40635  	}else{
 40636  		/* Hard link */
 40637  		rc = link(zSrc, zTarget);
 40638  	}
 40639  	return rc == 0 ? JX9_OK : -1;
 40640  }
 40641  /* int (*xChroot)(const char *) */
 40642  static int UnixVfs_chroot(const char *zRootDir)
 40643  {
 40644  	int rc;
 40645  	rc = chroot(zRootDir);
 40646  	return rc == 0 ? JX9_OK : -1;
 40647  }
 40648  /* Export the UNIX vfs */
 40649  static const jx9_vfs sUnixVfs = {
 40650  	"Unix_vfs", 
 40651  	JX9_VFS_VERSION, 
 40652  	UnixVfs_chdir,    /* int (*xChdir)(const char *) */
 40653  	UnixVfs_chroot,   /* int (*xChroot)(const char *); */
 40654  	UnixVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
 40655  	UnixVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
 40656  	UnixVfs_rmdir,    /* int (*xRmdir)(const char *) */ 
 40657  	UnixVfs_isdir,    /* int (*xIsdir)(const char *) */
 40658  	UnixVfs_Rename,   /* int (*xRename)(const char *, const char *) */
 40659  	UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
 40660  	UnixVfs_Sleep,    /* int (*xSleep)(unsigned int) */
 40661  	UnixVfs_unlink,   /* int (*xUnlink)(const char *) */
 40662  	UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
 40663  	UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
 40664  	UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
 40665  	UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
 40666  	0,             /* jx9_int64 (*xFreeSpace)(const char *) */
 40667  	0,             /* jx9_int64 (*xTotalSpace)(const char *) */
 40668  	UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
 40669  	UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
 40670  	UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
 40671  	UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
 40672  	UnixVfs_Stat,  /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
 40673  	UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
 40674  	UnixVfs_isfile,     /* int (*xIsfile)(const char *) */
 40675  	UnixVfs_islink,     /* int (*xIslink)(const char *) */
 40676  	UnixVfs_isreadable, /* int (*xReadable)(const char *) */
 40677  	UnixVfs_iswritable, /* int (*xWritable)(const char *) */
 40678  	UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
 40679  	UnixVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
 40680  	UnixVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
 40681  	UnixVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */ 
 40682  	UnixVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
 40683  	UnixVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
 40684  	UnixVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
 40685  	UnixVfs_link,       /* int (*xLink)(const char *, const char *, int) */
 40686  	UnixVfs_Umask,      /* int (*xUmask)(int) */
 40687  	UnixVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
 40688  	UnixVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
 40689  	UnixVfs_uid, /* int (*xUid)(void) */
 40690  	UnixVfs_gid, /* int (*xGid)(void) */
 40691  	UnixVfs_Username,    /* void (*xUsername)(jx9_context *) */
 40692  	0 /* int (*xExec)(const char *, jx9_context *) */
 40693  };
 40694  /* UNIX File IO */
 40695  #define JX9_UNIX_OPEN_MODE	0640 /* Default open mode */
 40696  /* int (*xOpen)(const char *, int, jx9_value *, void **) */
 40697  static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
 40698  {
 40699  	int iOpen = O_RDONLY;
 40700  	int fd;
 40701  	/* Set the desired flags according to the open mode */
 40702  	if( iOpenMode & JX9_IO_OPEN_CREATE ){
 40703  		/* Open existing file, or create if it doesn't exist */
 40704  		iOpen = O_CREAT;
 40705  		if( iOpenMode & JX9_IO_OPEN_TRUNC ){
 40706  			/* If the specified file exists and is writable, the function overwrites the file */
 40707  			iOpen |= O_TRUNC;
 40708  			SXUNUSED(pResource); /* cc warning */
 40709  		}
 40710  	}else if( iOpenMode & JX9_IO_OPEN_EXCL ){
 40711  		/* Creates a new file, only if it does not already exist.
 40712  		* If the file exists, it fails.
 40713  		*/
 40714  		iOpen = O_CREAT|O_EXCL;
 40715  	}else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
 40716  		/* Opens a file and truncates it so that its size is zero bytes
 40717  		 * The file must exist.
 40718  		 */
 40719  		iOpen = O_RDWR|O_TRUNC;
 40720  	}
 40721  	if( iOpenMode & JX9_IO_OPEN_RDWR ){
 40722  		/* Read+Write access */
 40723  		iOpen &= ~O_RDONLY;
 40724  		iOpen |= O_RDWR;
 40725  	}else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
 40726  		/* Write only access */
 40727  		iOpen &= ~O_RDONLY;
 40728  		iOpen |= O_WRONLY;
 40729  	}
 40730  	if( iOpenMode & JX9_IO_OPEN_APPEND ){
 40731  		/* Append mode */
 40732  		iOpen |= O_APPEND;
 40733  	}
 40734  #ifdef O_TEMP
 40735  	if( iOpenMode & JX9_IO_OPEN_TEMP ){
 40736  		/* File is temporary */
 40737  		iOpen |= O_TEMP;
 40738  	}
 40739  #endif
 40740  	/* Open the file now */
 40741  	fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
 40742  	if( fd < 0 ){
 40743  		/* IO error */
 40744  		return -1;
 40745  	}
 40746  	/* Save the handle */
 40747  	*ppHandle = SX_INT_TO_PTR(fd);
 40748  	return JX9_OK;
 40749  }
 40750  /* int (*xOpenDir)(const char *, jx9_value *, void **) */
 40751  static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
 40752  {
 40753  	DIR *pDir;
 40754  	/* Open the target directory */
 40755  	pDir = opendir(zPath);
 40756  	if( pDir == 0 ){
 40757  		pResource = 0; /* Compiler warning */
 40758  		return -1;
 40759  	}
 40760  	/* Save our structure */
 40761  	*ppHandle = pDir;
 40762  	return JX9_OK;
 40763  }
 40764  /* void (*xCloseDir)(void *) */
 40765  static void UnixDir_Close(void *pUserData)
 40766  {
 40767  	closedir((DIR *)pUserData);
 40768  }
 40769  /* void (*xClose)(void *); */
 40770  static void UnixFile_Close(void *pUserData)
 40771  {
 40772  	close(SX_PTR_TO_INT(pUserData));
 40773  }
 40774  /* int (*xReadDir)(void *, jx9_context *) */
 40775  static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
 40776  {
 40777  	DIR *pDir = (DIR *)pUserData;
 40778  	struct dirent *pEntry;
 40779  	char *zName = 0; /* cc warning */
 40780  	sxu32 n = 0;
 40781  	for(;;){
 40782  		pEntry = readdir(pDir);
 40783  		if( pEntry == 0 ){
 40784  			/* No more entries to process */
 40785  			return -1;
 40786  		}
 40787  		zName = pEntry->d_name; 
 40788  		n = SyStrlen(zName);
 40789  		/* Ignore '.' && '..' */
 40790  		if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
 40791  			break;
 40792  		}
 40793  		/* Next entry */
 40794  	}
 40795  	/* Return the current file name */
 40796  	jx9_result_string(pCtx, zName, (int)n);
 40797  	return JX9_OK;
 40798  }
 40799  /* void (*xRewindDir)(void *) */
 40800  static void UnixDir_Rewind(void *pUserData)
 40801  {
 40802  	rewinddir((DIR *)pUserData);
 40803  }
 40804  /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
 40805  static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
 40806  {
 40807  	ssize_t nRd;
 40808  	nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
 40809  	if( nRd < 1 ){
 40810  		/* EOF or IO error */
 40811  		return -1;
 40812  	}
 40813  	return (jx9_int64)nRd;
 40814  }
 40815  /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
 40816  static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
 40817  {
 40818  	const char *zData = (const char *)pBuffer;
 40819  	int fd = SX_PTR_TO_INT(pUserData);
 40820  	jx9_int64 nCount;
 40821  	ssize_t nWr;
 40822  	nCount = 0;
 40823  	for(;;){
 40824  		if( nWrite < 1 ){
 40825  			break;
 40826  		}
 40827  		nWr = write(fd, zData, (size_t)nWrite);
 40828  		if( nWr < 1 ){
 40829  			/* IO error */
 40830  			break;
 40831  		}
 40832  		nWrite -= nWr;
 40833  		nCount += nWr;
 40834  		zData += nWr;
 40835  	}
 40836  	if( nWrite > 0 ){
 40837  		return -1;
 40838  	}
 40839  	return nCount;
 40840  }
 40841  /* int (*xSeek)(void *, jx9_int64, int) */
 40842  static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
 40843  {
 40844  	off_t iNew;
 40845  	switch(whence){
 40846  	case 1:/*SEEK_CUR*/
 40847  		whence = SEEK_CUR;
 40848  		break;
 40849  	case 2: /* SEEK_END */
 40850  		whence = SEEK_END;
 40851  		break;
 40852  	case 0: /* SEEK_SET */
 40853  	default:
 40854  		whence = SEEK_SET;
 40855  		break;
 40856  	}
 40857  	iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
 40858  	if( iNew < 0 ){
 40859  		return -1;
 40860  	}
 40861  	return JX9_OK;
 40862  }
 40863  /* int (*xLock)(void *, int) */
 40864  static int UnixFile_Lock(void *pUserData, int lock_type)
 40865  {
 40866  	int fd = SX_PTR_TO_INT(pUserData);
 40867  	int rc = JX9_OK; /* cc warning */
 40868  	if( lock_type < 0 ){
 40869  		/* Unlock the file */
 40870  		rc = flock(fd, LOCK_UN);
 40871  	}else{
 40872  		if( lock_type == 1 ){
 40873  			/* Exculsive lock */
 40874  			rc = flock(fd, LOCK_EX);
 40875  		}else{
 40876  			/* Shared lock */
 40877  			rc = flock(fd, LOCK_SH);
 40878  		}
 40879  	}
 40880  	return !rc ? JX9_OK : -1;
 40881  }
 40882  /* jx9_int64 (*xTell)(void *) */
 40883  static jx9_int64 UnixFile_Tell(void *pUserData)
 40884  {
 40885  	off_t iNew;
 40886  	iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
 40887  	return (jx9_int64)iNew;
 40888  }
 40889  /* int (*xTrunc)(void *, jx9_int64) */
 40890  static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
 40891  {
 40892  	int rc;
 40893  	rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
 40894  	if( rc != 0 ){
 40895  		return -1;
 40896  	}
 40897  	return JX9_OK;
 40898  }
 40899  /* int (*xSync)(void *); */
 40900  static int UnixFile_Sync(void *pUserData)
 40901  {
 40902  	int rc;	
 40903  	rc = fsync(SX_PTR_TO_INT(pUserData));
 40904  	return rc == 0 ? JX9_OK : - 1;
 40905  }
 40906  /* int (*xStat)(void *, jx9_value *, jx9_value *) */
 40907  static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
 40908  {
 40909  	struct stat st;
 40910  	int rc;
 40911  	rc = fstat(SX_PTR_TO_INT(pUserData), &st);
 40912  	if( rc != 0 ){ 
 40913  	 return -1;
 40914  	}
 40915  	/* dev */
 40916  	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
 40917  	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
 40918  	/* ino */
 40919  	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
 40920  	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
 40921  	/* mode */
 40922  	jx9_value_int(pWorker, (int)st.st_mode);
 40923  	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
 40924  	/* nlink */
 40925  	jx9_value_int(pWorker, (int)st.st_nlink);
 40926  	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
 40927  	/* uid, gid, rdev */
 40928  	jx9_value_int(pWorker, (int)st.st_uid);
 40929  	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
 40930  	jx9_value_int(pWorker, (int)st.st_gid);
 40931  	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
 40932  	jx9_value_int(pWorker, (int)st.st_rdev);
 40933  	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
 40934  	/* size */
 40935  	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
 40936  	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
 40937  	/* atime */
 40938  	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
 40939  	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
 40940  	/* mtime */
 40941  	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
 40942  	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
 40943  	/* ctime */
 40944  	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
 40945  	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
 40946  	/* blksize, blocks */
 40947  	jx9_value_int(pWorker, (int)st.st_blksize);		
 40948  	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
 40949  	jx9_value_int(pWorker, (int)st.st_blocks);
 40950  	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
 40951  	return JX9_OK;
 40952  }
 40953  /* Export the file:// stream */
 40954  static const jx9_io_stream sUnixFileStream = {
 40955  	"file", /* Stream name */
 40956  	JX9_IO_STREAM_VERSION, 
 40957  	UnixFile_Open,  /* xOpen */
 40958  	UnixDir_Open,   /* xOpenDir */
 40959  	UnixFile_Close, /* xClose */
 40960  	UnixDir_Close,  /* xCloseDir */
 40961  	UnixFile_Read,  /* xRead */
 40962  	UnixDir_Read,   /* xReadDir */
 40963  	UnixFile_Write, /* xWrite */
 40964  	UnixFile_Seek,  /* xSeek */
 40965  	UnixFile_Lock,  /* xLock */
 40966  	UnixDir_Rewind, /* xRewindDir */
 40967  	UnixFile_Tell,  /* xTell */
 40968  	UnixFile_Trunc, /* xTrunc */
 40969  	UnixFile_Sync,  /* xSeek */
 40970  	UnixFile_Stat   /* xStat */
 40971  };
 40972  #endif /* __WINNT__/__UNIXES__ */
 40973  #endif /* JX9_DISABLE_DISK_IO */
 40974  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 40975  /* 
 40976   * Export the builtin vfs.
 40977   * Return a pointer to the builtin vfs if available.
 40978   * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
 40979   * Note:
 40980   *  The built-in vfs is always available for Windows/UNIX systems.
 40981   * Note:
 40982   *  If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
 40983   *  directives defined then this function return the null_vfs instead.
 40984   */
 40985  JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
 40986  {
 40987  #ifndef JX9_DISABLE_BUILTIN_FUNC
 40988  #ifdef JX9_DISABLE_DISK_IO
 40989  	return &null_vfs;
 40990  #else
 40991  #ifdef __WINNT__
 40992  	return &sWinVfs;
 40993  #elif defined(__UNIXES__)
 40994  	return &sUnixVfs;
 40995  #else
 40996  	return &null_vfs;
 40997  #endif /* __WINNT__/__UNIXES__ */
 40998  #endif /*JX9_DISABLE_DISK_IO*/
 40999  #else
 41000  	return &null_vfs;
 41001  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 41002  }
 41003  #ifndef JX9_DISABLE_BUILTIN_FUNC
 41004  #ifndef JX9_DISABLE_DISK_IO
 41005  /*
 41006   * The following defines are mostly used by the UNIX built and have
 41007   * no particular meaning on windows.
 41008   */
 41009  #ifndef STDIN_FILENO
 41010  #define STDIN_FILENO	0
 41011  #endif
 41012  #ifndef STDOUT_FILENO
 41013  #define STDOUT_FILENO	1
 41014  #endif
 41015  #ifndef STDERR_FILENO
 41016  #define STDERR_FILENO	2
 41017  #endif
 41018  /*
 41019   * jx9:// Accessing various I/O streams
 41020   * According to the JX9 langage reference manual
 41021   * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
 41022   * and output streams, the standard input, output and error file descriptors.
 41023   * jx9://stdin, jx9://stdout and jx9://stderr:
 41024   *  Allow direct access to the corresponding input or output stream of the JX9 process.
 41025   *  The stream references a duplicate file descriptor, so if you open jx9://stdin and later
 41026   *  close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
 41027   *  jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
 41028   * jx9://output
 41029   *  jx9://output is a write-only stream that allows you to write to the output buffer 
 41030   *  mechanism in the same way as print and print. 
 41031   */
 41032  typedef struct jx9_stream_data jx9_stream_data;
 41033  /* Supported IO streams */
 41034  #define JX9_IO_STREAM_STDIN  1 /* jx9://stdin */
 41035  #define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
 41036  #define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
 41037  #define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
 41038   /* The following structure is the private data associated with the jx9:// stream */
 41039  struct jx9_stream_data
 41040  {
 41041  	jx9_vm *pVm; /* VM that own this instance */
 41042  	int iType;   /* Stream type */
 41043  	union{
 41044  		void *pHandle; /* Stream handle */
 41045  		jx9_output_consumer sConsumer; /* VM output consumer */
 41046  	}x;
 41047  };
 41048  /*
 41049   * Allocate a new instance of the jx9_stream_data structure.
 41050   */
 41051  static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
 41052  {
 41053  	jx9_stream_data *pData;
 41054  	if( pVm == 0 ){
 41055  		return 0;
 41056  	}
 41057  	/* Allocate a new instance */
 41058  	pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
 41059  	if( pData == 0 ){
 41060  		return 0;
 41061  	}
 41062  	/* Zero the structure */
 41063  	SyZero(pData, sizeof(jx9_stream_data));
 41064  	/* Initialize fields */
 41065  	pData->iType = iType;
 41066  	if( iType == JX9_IO_STREAM_OUTPUT ){
 41067  		/* Point to the default VM consumer routine. */
 41068  		pData->x.sConsumer = pVm->sVmConsumer; 
 41069  	}else{
 41070  #ifdef __WINNT__
 41071  		DWORD nChannel;
 41072  		switch(iType){
 41073  		case JX9_IO_STREAM_STDOUT:	nChannel = STD_OUTPUT_HANDLE; break;
 41074  		case JX9_IO_STREAM_STDERR:  nChannel = STD_ERROR_HANDLE; break;
 41075  		default:
 41076  			nChannel = STD_INPUT_HANDLE; 
 41077  			break;
 41078  		}
 41079  		pData->x.pHandle = GetStdHandle(nChannel);
 41080  #else
 41081  		/* Assume an UNIX system */
 41082  		int ifd = STDIN_FILENO;
 41083  		switch(iType){
 41084  		case JX9_IO_STREAM_STDOUT:  ifd = STDOUT_FILENO; break;
 41085  		case JX9_IO_STREAM_STDERR:  ifd = STDERR_FILENO; break;
 41086  		default:
 41087  			break;
 41088  		}
 41089  		pData->x.pHandle = SX_INT_TO_PTR(ifd);
 41090  #endif
 41091  	}
 41092  	pData->pVm = pVm;
 41093  	return pData;
 41094  }
 41095  /* 
 41096   * Implementation of the jx9:// IO streams routines
 41097   * Authors:
 41098   *  Symisc Systems, devel@symisc.net.
 41099   *  Copyright (C) Symisc Systems, http://jx9.symisc.net
 41100   * Status:
 41101   *   Stable.
 41102   */
 41103  /* int (*xOpen)(const char *, int, jx9_value *, void **) */
 41104  static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
 41105  {
 41106  	jx9_stream_data *pData;
 41107  	SyString sStream;
 41108  	SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
 41109  	/* Trim leading and trailing white spaces */
 41110  	SyStringFullTrim(&sStream);
 41111  	/* Stream to open */
 41112  	if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
 41113  		iMode = JX9_IO_STREAM_STDIN;
 41114  	}else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
 41115  		iMode = JX9_IO_STREAM_OUTPUT;
 41116  	}else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
 41117  		iMode = JX9_IO_STREAM_STDOUT;
 41118  	}else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
 41119  		iMode = JX9_IO_STREAM_STDERR;
 41120  	}else{
 41121  		/* unknown stream name */
 41122  		return -1;
 41123  	}
 41124  	/* Create our handle */
 41125  	pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
 41126  	if( pData == 0 ){
 41127  		return -1;
 41128  	}
 41129  	/* Make the handle public */
 41130  	*ppHandle = (void *)pData;
 41131  	return JX9_OK;
 41132  }
 41133  /* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
 41134  static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
 41135  {
 41136  	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
 41137  	if( pData == 0 ){
 41138  		return -1;
 41139  	}
 41140  	if( pData->iType != JX9_IO_STREAM_STDIN ){
 41141  		/* Forbidden */
 41142  		return -1;
 41143  	}
 41144  #ifdef __WINNT__
 41145  	{
 41146  		DWORD nRd;
 41147  		BOOL rc;
 41148  		rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
 41149  		if( !rc ){
 41150  			/* IO error */
 41151  			return -1;
 41152  		}
 41153  		return (jx9_int64)nRd;
 41154  	}
 41155  #elif defined(__UNIXES__)
 41156  	{
 41157  		ssize_t nRd;
 41158  		int fd;
 41159  		fd = SX_PTR_TO_INT(pData->x.pHandle);
 41160  		nRd = read(fd, pBuffer, (size_t)nDatatoRead);
 41161  		if( nRd < 1 ){
 41162  			return -1;
 41163  		}
 41164  		return (jx9_int64)nRd;
 41165  	}
 41166  #else
 41167  	return -1;
 41168  #endif
 41169  }
 41170  /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
 41171  static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
 41172  {
 41173  	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
 41174  	if( pData == 0 ){
 41175  		return -1;
 41176  	}
 41177  	if( pData->iType == JX9_IO_STREAM_STDIN ){
 41178  		/* Forbidden */
 41179  		return -1;
 41180  	}else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
 41181  		jx9_output_consumer *pCons = &pData->x.sConsumer;
 41182  		int rc;
 41183  		/* Call the vm output consumer */
 41184  		rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
 41185  		if( rc == JX9_ABORT ){
 41186  			return -1;
 41187  		}
 41188  		return nWrite;
 41189  	}
 41190  #ifdef __WINNT__
 41191  	{
 41192  		DWORD nWr;
 41193  		BOOL rc;
 41194  		rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
 41195  		if( !rc ){
 41196  			/* IO error */
 41197  			return -1;
 41198  		}
 41199  		return (jx9_int64)nWr;
 41200  	}
 41201  #elif defined(__UNIXES__)
 41202  	{
 41203  		ssize_t nWr;
 41204  		int fd;
 41205  		fd = SX_PTR_TO_INT(pData->x.pHandle);
 41206  		nWr = write(fd, pBuf, (size_t)nWrite);
 41207  		if( nWr < 1 ){
 41208  			return -1;
 41209  		}
 41210  		return (jx9_int64)nWr;
 41211  	}
 41212  #else
 41213  	return -1;
 41214  #endif
 41215  }
 41216  /* void (*xClose)(void *) */
 41217  static void JX9StreamData_Close(void *pHandle)
 41218  {
 41219  	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
 41220  	jx9_vm *pVm;
 41221  	if( pData == 0 ){
 41222  		return;
 41223  	}
 41224  	pVm = pData->pVm;
 41225  	/* Free the instance */
 41226  	SyMemBackendFree(&pVm->sAllocator, pData);
 41227  }
 41228  /* Export the jx9:// stream */
 41229  static const jx9_io_stream sjx9Stream = {
 41230  	"jx9", 
 41231  	JX9_IO_STREAM_VERSION, 
 41232  	JX9StreamData_Open,  /* xOpen */
 41233  	0,   /* xOpenDir */
 41234  	JX9StreamData_Close, /* xClose */
 41235  	0,  /* xCloseDir */
 41236  	JX9StreamData_Read,  /* xRead */
 41237  	0,  /* xReadDir */
 41238  	JX9StreamData_Write, /* xWrite */
 41239  	0,  /* xSeek */
 41240  	0,  /* xLock */
 41241  	0,  /* xRewindDir */
 41242  	0,  /* xTell */
 41243  	0,  /* xTrunc */
 41244  	0,  /* xSeek */
 41245  	0   /* xStat */
 41246  };
 41247  #endif /* JX9_DISABLE_DISK_IO */
 41248  /*
 41249   * Return TRUE if we are dealing with the jx9:// stream.
 41250   * FALSE otherwise.
 41251   */
 41252  static int is_jx9_stream(const jx9_io_stream *pStream)
 41253  {
 41254  #ifndef JX9_DISABLE_DISK_IO
 41255  	return pStream == &sjx9Stream;
 41256  #else
 41257  	SXUNUSED(pStream); /* cc warning */
 41258  	return 0;
 41259  #endif /* JX9_DISABLE_DISK_IO */
 41260  }
 41261  
 41262  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 41263  /*
 41264   * Export the IO routines defined above and the built-in IO streams
 41265   * [i.e: file://, jx9://].
 41266   * Note:
 41267   *  If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
 41268   *  defined then this function is a no-op.
 41269   */
 41270  JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
 41271  {
 41272  #ifndef JX9_DISABLE_BUILTIN_FUNC
 41273  	      /* VFS functions */
 41274  	static const jx9_builtin_func aVfsFunc[] = {
 41275  		{"chdir",   jx9Vfs_chdir   }, 
 41276  		{"chroot",  jx9Vfs_chroot  }, 
 41277  		{"getcwd",  jx9Vfs_getcwd  }, 
 41278  		{"rmdir",   jx9Vfs_rmdir   }, 
 41279  		{"is_dir",  jx9Vfs_is_dir  }, 
 41280  		{"mkdir",   jx9Vfs_mkdir   }, 
 41281  		{"rename",  jx9Vfs_rename  }, 
 41282  		{"realpath", jx9Vfs_realpath}, 
 41283  		{"sleep",   jx9Vfs_sleep   }, 
 41284  		{"usleep",  jx9Vfs_usleep  }, 
 41285  		{"unlink",  jx9Vfs_unlink  }, 
 41286  		{"delete",  jx9Vfs_unlink  }, 
 41287  		{"chmod",   jx9Vfs_chmod   }, 
 41288  		{"chown",   jx9Vfs_chown   }, 
 41289  		{"chgrp",   jx9Vfs_chgrp   }, 
 41290  		{"disk_free_space", jx9Vfs_disk_free_space  }, 
 41291  		{"disk_total_space", jx9Vfs_disk_total_space}, 
 41292  		{"file_exists", jx9Vfs_file_exists }, 
 41293  		{"filesize",    jx9Vfs_file_size   }, 
 41294  		{"fileatime",   jx9Vfs_file_atime  }, 
 41295  		{"filemtime",   jx9Vfs_file_mtime  }, 
 41296  		{"filectime",   jx9Vfs_file_ctime  }, 
 41297  		{"is_file",     jx9Vfs_is_file  }, 
 41298  		{"is_link",     jx9Vfs_is_link  }, 
 41299  		{"is_readable", jx9Vfs_is_readable   }, 
 41300  		{"is_writable", jx9Vfs_is_writable   }, 
 41301  		{"is_executable", jx9Vfs_is_executable}, 
 41302  		{"filetype",    jx9Vfs_filetype }, 
 41303  		{"stat",        jx9Vfs_stat     }, 
 41304  		{"lstat",       jx9Vfs_lstat    }, 
 41305  		{"getenv",      jx9Vfs_getenv   }, 
 41306  		{"setenv",      jx9Vfs_putenv   }, 
 41307  		{"putenv",      jx9Vfs_putenv   }, 
 41308  		{"touch",       jx9Vfs_touch    }, 
 41309  		{"link",        jx9Vfs_link     }, 
 41310  		{"symlink",     jx9Vfs_symlink  }, 
 41311  		{"umask",       jx9Vfs_umask    }, 
 41312  		{"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir }, 
 41313  		{"get_current_user", jx9Vfs_get_current_user }, 
 41314  		{"getpid",      jx9Vfs_getmypid }, 
 41315  		{"getuid",      jx9Vfs_getmyuid }, 
 41316  		{"getgid",      jx9Vfs_getmygid }, 
 41317  		{"uname",       jx9Vfs_uname}, 
 41318  		     /* Path processing */ 
 41319  		{"dirname",     jx9Builtin_dirname  }, 
 41320  		{"basename",    jx9Builtin_basename }, 
 41321  		{"pathinfo",    jx9Builtin_pathinfo }, 
 41322  		{"strglob",     jx9Builtin_strglob  }, 
 41323  		{"fnmatch",     jx9Builtin_fnmatch  }, 
 41324  		     /* ZIP processing */
 41325  		{"zip_open",    jx9Builtin_zip_open }, 
 41326  		{"zip_close",   jx9Builtin_zip_close}, 
 41327  		{"zip_read",    jx9Builtin_zip_read }, 
 41328  		{"zip_entry_open", jx9Builtin_zip_entry_open }, 
 41329  		{"zip_entry_close", jx9Builtin_zip_entry_close}, 
 41330  		{"zip_entry_name", jx9Builtin_zip_entry_name }, 
 41331  		{"zip_entry_filesize",      jx9Builtin_zip_entry_filesize       }, 
 41332  		{"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize }, 
 41333  		{"zip_entry_read", jx9Builtin_zip_entry_read }, 
 41334  		{"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor}, 
 41335  		{"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
 41336  	};
 41337  	    /* IO stream functions */
 41338  	static const jx9_builtin_func aIOFunc[] = {
 41339  		{"ftruncate", jx9Builtin_ftruncate }, 
 41340  		{"fseek",     jx9Builtin_fseek  }, 
 41341  		{"ftell",     jx9Builtin_ftell  }, 
 41342  		{"rewind",    jx9Builtin_rewind }, 
 41343  		{"fflush",    jx9Builtin_fflush }, 
 41344  		{"feof",      jx9Builtin_feof   }, 
 41345  		{"fgetc",     jx9Builtin_fgetc  }, 
 41346  		{"fgets",     jx9Builtin_fgets  }, 
 41347  		{"fread",     jx9Builtin_fread  }, 
 41348  		{"fgetcsv",   jx9Builtin_fgetcsv}, 
 41349  		{"fgetss",    jx9Builtin_fgetss }, 
 41350  		{"readdir",   jx9Builtin_readdir}, 
 41351  		{"rewinddir", jx9Builtin_rewinddir }, 
 41352  		{"closedir",  jx9Builtin_closedir}, 
 41353  		{"opendir",   jx9Builtin_opendir }, 
 41354  		{"readfile",  jx9Builtin_readfile}, 
 41355  		{"file_get_contents", jx9Builtin_file_get_contents}, 
 41356  		{"file_put_contents", jx9Builtin_file_put_contents}, 
 41357  		{"file",      jx9Builtin_file   }, 
 41358  		{"copy",      jx9Builtin_copy   }, 
 41359  		{"fstat",     jx9Builtin_fstat  }, 
 41360  		{"fwrite",    jx9Builtin_fwrite }, 
 41361  		{"fputs",     jx9Builtin_fwrite }, 
 41362  		{"flock",     jx9Builtin_flock  }, 
 41363  		{"fclose",    jx9Builtin_fclose }, 
 41364  		{"fopen",     jx9Builtin_fopen  }, 
 41365  		{"fpassthru", jx9Builtin_fpassthru }, 
 41366  		{"fputcsv",   jx9Builtin_fputcsv }, 
 41367  		{"fprintf",   jx9Builtin_fprintf }, 
 41368  #if !defined(JX9_DISABLE_HASH_FUNC)
 41369  		{"md5_file",  jx9Builtin_md5_file}, 
 41370  		{"sha1_file", jx9Builtin_sha1_file}, 
 41371  #endif /* JX9_DISABLE_HASH_FUNC */
 41372  		{"parse_ini_file", jx9Builtin_parse_ini_file}, 
 41373  		{"vfprintf",  jx9Builtin_vfprintf}
 41374  	};
 41375  	const jx9_io_stream *pFileStream = 0;
 41376  	sxu32 n = 0;
 41377  	/* Register the functions defined above */
 41378  	for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
 41379  		jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
 41380  	}
 41381  	for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
 41382  		jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
 41383  	}
 41384  #ifndef JX9_DISABLE_DISK_IO
 41385  	/* Register the file stream if available */
 41386  #ifdef __WINNT__
 41387  	pFileStream = &sWinFileStream;
 41388  #elif defined(__UNIXES__)
 41389  	pFileStream = &sUnixFileStream;
 41390  #endif
 41391  	/* Install the jx9:// stream */
 41392  	jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
 41393  #endif /* JX9_DISABLE_DISK_IO */
 41394  	if( pFileStream ){
 41395  		/* Install the file:// stream */
 41396  		jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
 41397  	}
 41398  #else
 41399     SXUNUSED(pVm); /* cc warning */
 41400  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 41401  	return SXRET_OK;
 41402  }
 41403  /*
 41404   * Export the STDIN handle.
 41405   */
 41406  JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
 41407  {
 41408  #ifndef JX9_DISABLE_BUILTIN_FUNC
 41409  #ifndef JX9_DISABLE_DISK_IO
 41410  	if( pVm->pStdin == 0  ){
 41411  		io_private *pIn;
 41412  		/* Allocate an IO private instance */
 41413  		pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
 41414  		if( pIn == 0 ){
 41415  			return 0;
 41416  		}
 41417  		InitIOPrivate(pVm, &sjx9Stream, pIn);
 41418  		/* Initialize the handle */
 41419  		pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
 41420  		/* Install the STDIN stream */
 41421  		pVm->pStdin = pIn;
 41422  		return pIn;
 41423  	}else{
 41424  		/* NULL or STDIN */
 41425  		return pVm->pStdin;
 41426  	}
 41427  #else
 41428  	return 0;
 41429  #endif
 41430  #else
 41431  	SXUNUSED(pVm); /* cc warning */
 41432  	return 0;
 41433  #endif
 41434  }
 41435  /*
 41436   * Export the STDOUT handle.
 41437   */
 41438  JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
 41439  {
 41440  #ifndef JX9_DISABLE_BUILTIN_FUNC
 41441  #ifndef JX9_DISABLE_DISK_IO
 41442  	if( pVm->pStdout == 0  ){
 41443  		io_private *pOut;
 41444  		/* Allocate an IO private instance */
 41445  		pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
 41446  		if( pOut == 0 ){
 41447  			return 0;
 41448  		}
 41449  		InitIOPrivate(pVm, &sjx9Stream, pOut);
 41450  		/* Initialize the handle */
 41451  		pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
 41452  		/* Install the STDOUT stream */
 41453  		pVm->pStdout = pOut;
 41454  		return pOut;
 41455  	}else{
 41456  		/* NULL or STDOUT */
 41457  		return pVm->pStdout;
 41458  	}
 41459  #else
 41460  	return 0;
 41461  #endif
 41462  #else
 41463  	SXUNUSED(pVm); /* cc warning */
 41464  	return 0;
 41465  #endif
 41466  }
 41467  /*
 41468   * Export the STDERR handle.
 41469   */
 41470  JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
 41471  {
 41472  #ifndef JX9_DISABLE_BUILTIN_FUNC
 41473  #ifndef JX9_DISABLE_DISK_IO
 41474  	if( pVm->pStderr == 0  ){
 41475  		io_private *pErr;
 41476  		/* Allocate an IO private instance */
 41477  		pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
 41478  		if( pErr == 0 ){
 41479  			return 0;
 41480  		}
 41481  		InitIOPrivate(pVm, &sjx9Stream, pErr);
 41482  		/* Initialize the handle */
 41483  		pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
 41484  		/* Install the STDERR stream */
 41485  		pVm->pStderr = pErr;
 41486  		return pErr;
 41487  	}else{
 41488  		/* NULL or STDERR */
 41489  		return pVm->pStderr;
 41490  	}
 41491  #else
 41492  	return 0;
 41493  #endif
 41494  #else
 41495  	SXUNUSED(pVm); /* cc warning */
 41496  	return 0;
 41497  #endif
 41498  }
 41499  
 41500  /*
 41501   * ----------------------------------------------------------
 41502   * File: jx9_vm.c
 41503   * MD5: beca4be65a9a49c932c356d7680034c9
 41504   * ----------------------------------------------------------
 41505   */
 41506  /*
 41507   * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
 41508   * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
 41509   * Version 1.7.2
 41510   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 41511   * please contact Symisc Systems via:
 41512   *       legal@symisc.net
 41513   *       licensing@symisc.net
 41514   *       contact@symisc.net
 41515   * or visit:
 41516   *      http://jx9.symisc.net/
 41517   */
 41518   /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
 41519  #ifndef JX9_AMALGAMATION
 41520  #include "jx9Int.h"
 41521  #endif
 41522  /*
 41523   * The code in this file implements execution method of the JX9 Virtual Machine.
 41524   * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program
 41525   * which is then executed by the virtual machine implemented here to do the work of the JX9
 41526   * statements.
 41527   * JX9 bytecode programs are similar in form to assembly language. The program consists
 41528   * of a linear sequence of operations .Each operation has an opcode and 3 operands.
 41529   * Operands P1 and P2 are integers where the first is signed while the second is unsigned.
 41530   * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually
 41531   * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions.
 41532   * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands.
 41533   * Computation results are stored on a stack. Each entry on the stack is of type jx9_value.
 41534   * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
 41535   * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
 41536   * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
 41537   * and so on.
 41538   * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
 41539   * Each jx9_value may cache multiple representations(string, integer etc.) of the same value.
 41540   * An implicit conversion from one type to the other occurs as necessary.
 41541   * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does
 41542   * the work of interpreting a JX9 bytecode program. But other routines are also provided
 41543   * to help in building up a program instruction by instruction.
 41544   */
 41545  /*
 41546   * Each active virtual machine frame is represented by an instance 
 41547   * of the following structure.
 41548   * VM Frame hold local variables and other stuff related to function call.
 41549   */
 41550  struct VmFrame
 41551  {
 41552  	VmFrame *pParent; /* Parent frame or NULL if global scope */
 41553  	void *pUserData;  /* Upper layer private data associated with this frame */
 41554  	SySet sLocal;     /* Local variables container (VmSlot instance) */
 41555  	jx9_vm *pVm;      /* VM that own this frame */
 41556  	SyHash hVar;      /* Variable hashtable for fast lookup */
 41557  	SySet sArg;       /* Function arguments container */
 41558  	sxi32 iFlags;     /* Frame configuration flags (See below)*/
 41559  	sxu32 iExceptionJump; /* Exception jump destination */
 41560  };
 41561  /*
 41562   * When a user defined variable is  garbage collected, memory object index
 41563   * is stored in an instance of the following structure and put in the free object
 41564   * table so that it can be reused again without allocating a new memory object.
 41565   */
 41566  typedef struct VmSlot VmSlot;
 41567  struct VmSlot
 41568  {
 41569  	sxu32 nIdx;      /* Index in pVm->aMemObj[] */ 
 41570  	void *pUserData; /* Upper-layer private data */
 41571  };
 41572  /*
 41573   * Each parsed URI is recorded and stored in an instance of the following structure.
 41574   * This structure and it's related routines are taken verbatim from the xHT project
 41575   * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
 41576   * the xHT project is developed internally by Symisc Systems.
 41577   */
 41578  typedef struct SyhttpUri SyhttpUri;
 41579  struct SyhttpUri 
 41580  { 
 41581  	SyString sHost;     /* Hostname or IP address */ 
 41582  	SyString sPort;     /* Port number */ 
 41583  	SyString sPath;     /* Mandatory resource path passed verbatim (Not decoded) */ 
 41584  	SyString sQuery;    /* Query part */	 
 41585  	SyString sFragment; /* Fragment part */ 
 41586  	SyString sScheme;   /* Scheme */ 
 41587  	SyString sUser;     /* Username */ 
 41588  	SyString sPass;     /* Password */
 41589  	SyString sRaw;      /* Raw URI */
 41590  };
 41591  /* 
 41592   * An instance of the following structure is used to record all MIME headers seen
 41593   * during a HTTP interaction. 
 41594   * This structure and it's related routines are taken verbatim from the xHT project
 41595   * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
 41596   * the xHT project is developed internally by Symisc Systems.
 41597   */  
 41598  typedef struct SyhttpHeader SyhttpHeader;
 41599  struct SyhttpHeader 
 41600  { 
 41601  	SyString sName;    /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */ 
 41602  	SyString sValue;   /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */ 
 41603  };
 41604  /*
 41605   * Supported HTTP methods.
 41606   */
 41607  #define HTTP_METHOD_GET  1 /* GET */
 41608  #define HTTP_METHOD_HEAD 2 /* HEAD */
 41609  #define HTTP_METHOD_POST 3 /* POST */
 41610  #define HTTP_METHOD_PUT  4 /* PUT */
 41611  #define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/
 41612  /*
 41613   * Supported HTTP protocol version.
 41614   */
 41615  #define HTTP_PROTO_10 1 /* HTTP/1.0 */
 41616  #define HTTP_PROTO_11 2 /* HTTP/1.1 */
 41617  /*
 41618   * Register a constant and it's associated expansion callback so that
 41619   * it can be expanded from the target JX9 program.
 41620   * The constant expansion mechanism under JX9 is extremely powerful yet
 41621   * simple and work as follows:
 41622   * Each registered constant have a C procedure associated with it.
 41623   * This procedure known as the constant expansion callback is responsible
 41624   * of expanding the invoked constant to the desired value, for example:
 41625   * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
 41626   * The "__OS__" constant procedure expands to the name of the host Operating Systems
 41627   * (Windows, Linux, ...) and so on.
 41628   * Please refer to the official documentation for additional information.
 41629   */
 41630  JX9_PRIVATE sxi32 jx9VmRegisterConstant(
 41631  	jx9_vm *pVm,            /* Target VM */
 41632  	const SyString *pName,  /* Constant name */
 41633  	ProcConstant xExpand,   /* Constant expansion callback */
 41634  	void *pUserData         /* Last argument to xExpand() */
 41635  	)
 41636  {
 41637  	jx9_constant *pCons;
 41638  	SyHashEntry *pEntry;
 41639  	char *zDupName;
 41640  	sxi32 rc;
 41641  	pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte);
 41642  	if( pEntry ){
 41643  		/* Overwrite the old definition and return immediately */
 41644  		pCons = (jx9_constant *)pEntry->pUserData;
 41645  		pCons->xExpand = xExpand;
 41646  		pCons->pUserData = pUserData;
 41647  		return SXRET_OK;
 41648  	}
 41649  	/* Allocate a new constant instance */
 41650  	pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant));
 41651  	if( pCons == 0 ){
 41652  		return 0;
 41653  	}
 41654  	/* Duplicate constant name */
 41655  	zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
 41656  	if( zDupName == 0 ){
 41657  		SyMemBackendPoolFree(&pVm->sAllocator, pCons);
 41658  		return 0;
 41659  	}
 41660  	/* Install the constant */
 41661  	SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
 41662  	pCons->xExpand = xExpand;
 41663  	pCons->pUserData = pUserData;
 41664  	rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
 41665  	if( rc != SXRET_OK ){
 41666  		SyMemBackendFree(&pVm->sAllocator, zDupName);
 41667  		SyMemBackendPoolFree(&pVm->sAllocator, pCons);
 41668  		return rc;
 41669  	}
 41670  	/* All done, constant can be invoked from JX9 code */
 41671  	return SXRET_OK;
 41672  }
 41673  /*
 41674   * Allocate a new foreign function instance.
 41675   * This function return SXRET_OK on success. Any other
 41676   * return value indicates failure.
 41677   * Please refer to the official documentation for an introduction to
 41678   * the foreign function mechanism.
 41679   */
 41680  static sxi32 jx9NewForeignFunction(
 41681  	jx9_vm *pVm,              /* Target VM */
 41682  	const SyString *pName,    /* Foreign function name */
 41683  	ProcHostFunction xFunc,  /* Foreign function implementation */
 41684  	void *pUserData,          /* Foreign function private data */
 41685  	jx9_user_func **ppOut     /* OUT: VM image of the foreign function */
 41686  	)
 41687  {
 41688  	jx9_user_func *pFunc;
 41689  	char *zDup;
 41690  	/* Allocate a new user function */
 41691  	pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func));
 41692  	if( pFunc == 0 ){
 41693  		return SXERR_MEM;
 41694  	}
 41695  	/* Duplicate function name */
 41696  	zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
 41697  	if( zDup == 0 ){
 41698  		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
 41699  		return SXERR_MEM;
 41700  	}
 41701  	/* Zero the structure */
 41702  	SyZero(pFunc, sizeof(jx9_user_func));
 41703  	/* Initialize structure fields */
 41704  	SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
 41705  	pFunc->pVm   = pVm;
 41706  	pFunc->xFunc = xFunc;
 41707  	pFunc->pUserData = pUserData;
 41708  	SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data));
 41709  	/* Write a pointer to the new function */
 41710  	*ppOut = pFunc;
 41711  	return SXRET_OK;
 41712  }
 41713  /*
 41714   * Install a foreign function and it's associated callback so that
 41715   * it can be invoked from the target JX9 code.
 41716   * This function return SXRET_OK on successful registration. Any other
 41717   * return value indicates failure.
 41718   * Please refer to the official documentation for an introduction to
 41719   * the foreign function mechanism.
 41720   */
 41721  JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
 41722  	jx9_vm *pVm,              /* Target VM */
 41723  	const SyString *pName,    /* Foreign function name */
 41724  	ProcHostFunction xFunc,  /* Foreign function implementation */
 41725  	void *pUserData           /* Foreign function private data */
 41726  	)
 41727  {
 41728  	jx9_user_func *pFunc;
 41729  	SyHashEntry *pEntry;
 41730  	sxi32 rc;
 41731  	/* Overwrite any previously registered function with the same name */
 41732  	pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
 41733  	if( pEntry ){
 41734  		pFunc = (jx9_user_func *)pEntry->pUserData;
 41735  		pFunc->pUserData = pUserData;
 41736  		pFunc->xFunc = xFunc;
 41737  		SySetReset(&pFunc->aAux);
 41738  		return SXRET_OK;
 41739  	}
 41740  	/* Create a new user function */
 41741  	rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
 41742  	if( rc != SXRET_OK ){
 41743  		return rc;
 41744  	}
 41745  	/* Install the function in the corresponding hashtable */
 41746  	rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
 41747  	if( rc != SXRET_OK ){
 41748  		SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
 41749  		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
 41750  		return rc;
 41751  	}
 41752  	/* User function successfully installed */
 41753  	return SXRET_OK;
 41754  }
 41755  /*
 41756   * Initialize a VM function.
 41757   */
 41758  JX9_PRIVATE sxi32 jx9VmInitFuncState(
 41759  	jx9_vm *pVm,        /* Target VM */
 41760  	jx9_vm_func *pFunc, /* Target Fucntion */
 41761  	const char *zName,  /* Function name */
 41762  	sxu32 nByte,        /* zName length */
 41763  	sxi32 iFlags,       /* Configuration flags */
 41764  	void *pUserData     /* Function private data */
 41765  	)
 41766  {
 41767  	/* Zero the structure */
 41768  	SyZero(pFunc, sizeof(jx9_vm_func));	
 41769  	/* Initialize structure fields */
 41770  	/* Arguments container */
 41771  	SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
 41772  	/* Static variable container */
 41773  	SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
 41774  	/* Bytecode container */
 41775  	SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
 41776      /* Preallocate some instruction slots */
 41777  	SySetAlloc(&pFunc->aByteCode, 0x10);
 41778  	pFunc->iFlags = iFlags;
 41779  	pFunc->pUserData = pUserData;
 41780  	SyStringInitFromBuf(&pFunc->sName, zName, nByte);
 41781  	return SXRET_OK;
 41782  }
 41783  /*
 41784   * Install a user defined function in the corresponding VM container.
 41785   */
 41786  JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
 41787  	jx9_vm *pVm,        /* Target VM */
 41788  	jx9_vm_func *pFunc, /* Target function */
 41789  	SyString *pName     /* Function name */
 41790  	)
 41791  {
 41792  	SyHashEntry *pEntry;
 41793  	sxi32 rc;
 41794  	if( pName == 0 ){
 41795  		/* Use the built-in name */
 41796  		pName = &pFunc->sName;
 41797  	}
 41798  	/* Check for duplicates (functions with the same name) first */
 41799  	pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
 41800  	if( pEntry ){
 41801  		jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData;
 41802  		if( pLink != pFunc ){
 41803  			/* Link */
 41804  			pFunc->pNextName = pLink;
 41805  			pEntry->pUserData = pFunc;
 41806  		}
 41807  		return SXRET_OK;
 41808  	}
 41809  	/* First time seen */
 41810  	pFunc->pNextName = 0;
 41811  	rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
 41812  	return rc;
 41813  }
 41814  /*
 41815   * Instruction builder interface.
 41816   */
 41817  JX9_PRIVATE sxi32 jx9VmEmitInstr(
 41818  	jx9_vm *pVm,  /* Target VM */
 41819  	sxi32 iOp,    /* Operation to perform */
 41820  	sxi32 iP1,    /* First operand */
 41821  	sxu32 iP2,    /* Second operand */
 41822  	void *p3,     /* Third operand */
 41823  	sxu32 *pIndex /* Instruction index. NULL otherwise */
 41824  	)
 41825  {
 41826  	VmInstr sInstr;
 41827  	sxi32 rc;
 41828  	/* Fill the VM instruction */
 41829  	sInstr.iOp = (sxu8)iOp; 
 41830  	sInstr.iP1 = iP1; 
 41831  	sInstr.iP2 = iP2; 
 41832  	sInstr.p3  = p3;  
 41833  	if( pIndex ){
 41834  		/* Instruction index in the bytecode array */
 41835  		*pIndex = SySetUsed(pVm->pByteContainer);
 41836  	}
 41837  	/* Finally, record the instruction */
 41838  	rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
 41839  	if( rc != SXRET_OK ){
 41840  		jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure");
 41841  		/* Fall throw */
 41842  	}
 41843  	return rc;
 41844  }
 41845  /*
 41846   * Swap the current bytecode container with the given one.
 41847   */
 41848  JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer)
 41849  {
 41850  	if( pContainer == 0 ){
 41851  		/* Point to the default container */
 41852  		pVm->pByteContainer = &pVm->aByteCode;
 41853  	}else{
 41854  		/* Change container */
 41855  		pVm->pByteContainer = &(*pContainer);
 41856  	}
 41857  	return SXRET_OK;
 41858  }
 41859  /*
 41860   * Return the current bytecode container.
 41861   */
 41862  JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
 41863  {
 41864  	return pVm->pByteContainer;
 41865  }
 41866  /*
 41867   * Extract the VM instruction rooted at nIndex.
 41868   */
 41869  JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
 41870  {
 41871  	VmInstr *pInstr;
 41872  	pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
 41873  	return pInstr;
 41874  }
 41875  /*
 41876   * Return the total number of VM instructions recorded so far.
 41877   */
 41878  JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
 41879  {
 41880  	return SySetUsed(pVm->pByteContainer);
 41881  }
 41882  /*
 41883   * Pop the last VM instruction.
 41884   */
 41885  JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
 41886  {
 41887  	return (VmInstr *)SySetPop(pVm->pByteContainer);
 41888  }
 41889  /*
 41890   * Peek the last VM instruction.
 41891   */
 41892  JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
 41893  {
 41894  	return (VmInstr *)SySetPeek(pVm->pByteContainer);
 41895  }
 41896  /*
 41897   * Allocate a new virtual machine frame.
 41898   */
 41899  static VmFrame * VmNewFrame(
 41900  	jx9_vm *pVm,              /* Target VM */
 41901  	void *pUserData          /* Upper-layer private data */
 41902  	)
 41903  {
 41904  	VmFrame *pFrame;
 41905  	/* Allocate a new vm frame */
 41906  	pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
 41907  	if( pFrame == 0 ){
 41908  		return 0;
 41909  	}
 41910  	/* Zero the structure */
 41911  	SyZero(pFrame, sizeof(VmFrame));
 41912  	/* Initialize frame fields */
 41913  	pFrame->pUserData = pUserData;
 41914  	pFrame->pVm = pVm;
 41915  	SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
 41916  	SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
 41917  	SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
 41918  	return pFrame;
 41919  }
 41920  /*
 41921   * Enter a VM frame.
 41922   */
 41923  static sxi32 VmEnterFrame(
 41924  	jx9_vm *pVm,               /* Target VM */
 41925  	void *pUserData,           /* Upper-layer private data */
 41926  	VmFrame **ppFrame          /* OUT: Top most active frame */
 41927  	)
 41928  {
 41929  	VmFrame *pFrame;
 41930  	/* Allocate a new frame */
 41931  	pFrame = VmNewFrame(&(*pVm), pUserData);
 41932  	if( pFrame == 0 ){
 41933  		return SXERR_MEM;
 41934  	}
 41935  	/* Link to the list of active VM frame */
 41936  	pFrame->pParent = pVm->pFrame;
 41937  	pVm->pFrame = pFrame;
 41938  	if( ppFrame ){
 41939  		/* Write a pointer to the new VM frame */
 41940  		*ppFrame = pFrame;
 41941  	}
 41942  	return SXRET_OK;
 41943  }
 41944  /*
 41945   * Link a foreign variable with the TOP most active frame.
 41946   * Refer to the JX9_OP_UPLINK instruction implementation for more
 41947   * information.
 41948   */
 41949  static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
 41950  {
 41951  	VmFrame *pTarget, *pFrame;
 41952  	SyHashEntry *pEntry = 0;
 41953  	sxi32 rc;
 41954  	/* Point to the upper frame */
 41955  	pFrame = pVm->pFrame;
 41956  	pTarget = pFrame;
 41957  	pFrame = pTarget->pParent;
 41958  	while( pFrame ){
 41959  		/* Query the current frame */
 41960  		pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
 41961  		if( pEntry ){
 41962  			/* Variable found */
 41963  			break;
 41964  		}		
 41965  		/* Point to the upper frame */
 41966  		pFrame = pFrame->pParent;
 41967  	}
 41968  	if( pEntry == 0 ){
 41969  		/* Inexistant variable */
 41970  		return SXERR_NOTFOUND;
 41971  	}
 41972  	/* Link to the current frame */
 41973  	rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData);
 41974  	return rc;
 41975  }
 41976  /*
 41977   * Leave the top-most active frame.
 41978   */
 41979  static void VmLeaveFrame(jx9_vm *pVm)
 41980  {
 41981  	VmFrame *pFrame = pVm->pFrame;
 41982  	if( pFrame ){
 41983  		/* Unlink from the list of active VM frame */
 41984  		pVm->pFrame = pFrame->pParent;
 41985  		if( pFrame->pParent  ){
 41986  			VmSlot  *aSlot;
 41987  			sxu32 n;
 41988  			/* Restore local variable to the free pool so that they can be reused again */
 41989  			aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
 41990  			for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){
 41991  				/* Unset the local variable */
 41992  				jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
 41993  			}
 41994  		}
 41995  		/* Release internal containers */
 41996  		SyHashRelease(&pFrame->hVar);
 41997  		SySetRelease(&pFrame->sArg);
 41998  		SySetRelease(&pFrame->sLocal);
 41999  		/* Release the whole structure */
 42000  		SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
 42001  	}
 42002  }
 42003  /*
 42004   * Compare two functions signature and return the comparison result.
 42005   */
 42006  static int VmOverloadCompare(SyString *pFirst, SyString *pSecond)
 42007  {
 42008  	const char *zSend = &pSecond->zString[pSecond->nByte];
 42009  	const char *zFend = &pFirst->zString[pFirst->nByte];
 42010  	const char *zSin = pSecond->zString;
 42011  	const char *zFin = pFirst->zString;
 42012  	const char *zPtr = zFin;
 42013  	for(;;){
 42014  		if( zFin >= zFend || zSin >= zSend ){
 42015  			break;
 42016  		}
 42017  		if( zFin[0] != zSin[0] ){
 42018  			/* mismatch */
 42019  			break;
 42020  		}
 42021  		zFin++;
 42022  		zSin++;
 42023  	}
 42024  	return (int)(zFin-zPtr);
 42025  }
 42026  /*
 42027   * Select the appropriate VM function for the current call context.
 42028   * This is the implementation of the powerful 'function overloading' feature
 42029   * introduced by the version 2 of the JX9 engine.
 42030   * Refer to the official documentation for more information.
 42031   */
 42032  static jx9_vm_func * VmOverload(
 42033  	jx9_vm *pVm,         /* Target VM */
 42034  	jx9_vm_func *pList,  /* Linked list of candidates for overloading */
 42035  	jx9_value *aArg,     /* Array of passed arguments */
 42036  	int nArg             /* Total number of passed arguments  */
 42037  	)
 42038  {
 42039  	int iTarget, i, j, iCur, iMax;
 42040  	jx9_vm_func *apSet[10];   /* Maximum number of candidates */
 42041  	jx9_vm_func *pLink;
 42042  	SyString sArgSig;
 42043  	SyBlob sSig;
 42044  
 42045  	pLink = pList;
 42046  	i = 0;
 42047  	/* Put functions expecting the same number of passed arguments */
 42048  	while( i < (int)SX_ARRAYSIZE(apSet) ){
 42049  		if( pLink == 0 ){
 42050  			break;
 42051  		}
 42052  		if( (int)SySetUsed(&pLink->aArgs) == nArg ){
 42053  			/* Candidate for overloading */
 42054  			apSet[i++] = pLink;
 42055  		}
 42056  		/* Point to the next entry */
 42057  		pLink = pLink->pNextName;
 42058  	}
 42059  	if( i < 1 ){
 42060  		/* No candidates, return the head of the list */
 42061  		return pList;
 42062  	}
 42063  	if( nArg < 1 || i < 2 ){
 42064  		/* Return the only candidate */
 42065  		return apSet[0];
 42066  	}
 42067  	/* Calculate function signature */
 42068  	SyBlobInit(&sSig, &pVm->sAllocator);
 42069  	for( j = 0 ; j < nArg ; j++ ){
 42070  		int c = 'n'; /* null */
 42071  		if( aArg[j].iFlags & MEMOBJ_HASHMAP ){
 42072  			/* Hashmap */
 42073  			c = 'h';
 42074  		}else if( aArg[j].iFlags & MEMOBJ_BOOL ){
 42075  			/* bool */
 42076  			c = 'b';
 42077  		}else if( aArg[j].iFlags & MEMOBJ_INT ){
 42078  			/* int */
 42079  			c = 'i';
 42080  		}else if( aArg[j].iFlags & MEMOBJ_STRING ){
 42081  			/* String */
 42082  			c = 's';
 42083  		}else if( aArg[j].iFlags & MEMOBJ_REAL ){
 42084  			/* Float */
 42085  			c = 'f';
 42086  		}
 42087  		if( c > 0 ){
 42088  			SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
 42089  		}
 42090  	}
 42091  	SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
 42092  	iTarget = 0;
 42093  	iMax = -1;
 42094  	/* Select the appropriate function */
 42095  	for( j = 0 ; j < i ; j++ ){
 42096  		/* Compare the two signatures */
 42097  		iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
 42098  		if( iCur > iMax ){
 42099  			iMax = iCur;
 42100  			iTarget = j;
 42101  		}
 42102  	}
 42103  	SyBlobRelease(&sSig);
 42104  	/* Appropriate function for the current call context */
 42105  	return apSet[iTarget];
 42106  }
 42107  /* 
 42108   * Dummy read-only buffer used for slot reservation.
 42109   */
 42110  static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */ 
 42111  /*
 42112   * Reserve a constant memory object.
 42113   * Return a pointer to the raw jx9_value on success. NULL on failure.
 42114   */
 42115  JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
 42116  {
 42117  	jx9_value *pObj;
 42118  	sxi32 rc;
 42119  	if( pIndex ){
 42120  		/* Object index in the object table */
 42121  		*pIndex = SySetUsed(&pVm->aLitObj);
 42122  	}
 42123  	/* Reserve a slot for the new object */
 42124  	rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
 42125  	if( rc != SXRET_OK ){
 42126  		/* If the supplied memory subsystem is so sick that we are unable to allocate
 42127  		 * a tiny chunk of memory, there is no much we can do here.
 42128  		 */
 42129  		return 0;
 42130  	}
 42131  	pObj = (jx9_value *)SySetPeek(&pVm->aLitObj);
 42132  	return pObj;
 42133  }
 42134  /*
 42135   * Reserve a memory object.
 42136   * Return a pointer to the raw jx9_value on success. NULL on failure.
 42137   */
 42138  static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
 42139  {
 42140  	jx9_value *pObj;
 42141  	sxi32 rc;
 42142  	if( pIndex ){
 42143  		/* Object index in the object table */
 42144  		*pIndex = SySetUsed(&pVm->aMemObj);
 42145  	}
 42146  	/* Reserve a slot for the new object */
 42147  	rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
 42148  	if( rc != SXRET_OK ){
 42149  		/* If the supplied memory subsystem is so sick that we are unable to allocate
 42150  		 * a tiny chunk of memory, there is no much we can do here.
 42151  		 */
 42152  		return 0;
 42153  	}
 42154  	pObj = (jx9_value *)SySetPeek(&pVm->aMemObj);
 42155  	return pObj;
 42156  }
 42157  /* Forward declaration */
 42158  static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
 42159  /*
 42160   * Built-in functions that cannot be implemented directly as foreign functions.
 42161   */
 42162  #define JX9_BUILTIN_LIB \
 42163  	"function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
 42164      "{"\
 42165  	"  if( func_num_args() < 1 ){ return FALSE; }"\
 42166  	"  $aDir = [];"\
 42167  	"  $pHandle = opendir($directory);"\
 42168  	"  if( $pHandle == FALSE ){ return FALSE; }"\
 42169  	"  while(FALSE !== ($pEntry = readdir($pHandle)) ){"\
 42170  	"      $aDir[] = $pEntry;"\
 42171  	"   }"\
 42172  	"  closedir($pHandle);"\
 42173  	"  if( $sort_order == SCANDIR_SORT_DESCENDING ){"\
 42174  	"      rsort($aDir);"\
 42175  	"  }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\
 42176  	"      sort($aDir);"\
 42177  	"  }"\
 42178  	"  return $aDir;"\
 42179  	"}"\
 42180  	"function glob(string $pattern, int $iFlags = 0){"\
 42181  	"/* Open the target directory */"\
 42182  	"$zDir = dirname($pattern);"\
 42183  	"if(!is_string($zDir) ){ $zDir = './'; }"\
 42184  	"$pHandle = opendir($zDir);"\
 42185  	"if( $pHandle == FALSE ){"\
 42186  	"   /* IO error while opening the current directory, return FALSE */"\
 42187  	"	return FALSE;"\
 42188  	"}"\
 42189  	"$pattern = basename($pattern);"\
 42190  	"$pArray = []; /* Empty array */"\
 42191  	"/* Loop throw available entries */"\
 42192  	"while( FALSE !== ($pEntry = readdir($pHandle)) ){"\
 42193  	" /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\
 42194  	"	$rc = strglob($pattern, $pEntry);"\
 42195  	"	if( $rc ){"\
 42196  	"	   if( is_dir($pEntry) ){"\
 42197  	"	      if( $iFlags & GLOB_MARK ){"\
 42198  	"		     /* Adds a slash to each directory returned */"\
 42199  	"			 $pEntry .= DIRECTORY_SEPARATOR;"\
 42200  	"		  }"\
 42201  	"	   }else if( $iFlags & GLOB_ONLYDIR ){"\
 42202  	"	     /* Not a directory, ignore */"\
 42203  	"		 continue;"\
 42204  	"	   }"\
 42205  	"	   /* Add the entry */"\
 42206  	"	   $pArray[] = $pEntry;"\
 42207  	"	}"\
 42208  	" }"\
 42209  	"/* Close the handle */"\
 42210  	"closedir($pHandle);"\
 42211  	"if( ($iFlags & GLOB_NOSORT) == 0 ){"\
 42212  	"  /* Sort the array */"\
 42213  	"  sort($pArray);"\
 42214  	"}"\
 42215  	"if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\
 42216  	"  /* Return the search pattern if no files matching were found */"\
 42217  	"  $pArray[] = $pattern;"\
 42218  	"}"\
 42219  	"/* Return the created array */"\
 42220  	"return $pArray;"\
 42221     "}"\
 42222     "/* Creates a temporary file */"\
 42223     "function tmpfile(){"\
 42224     "  /* Extract the temp directory */"\
 42225     "  $zTempDir = sys_get_temp_dir();"\
 42226     "  if( strlen($zTempDir) < 1 ){"\
 42227     "    /* Use the current dir */"\
 42228     "    $zTempDir = '.';"\
 42229     "  }"\
 42230     "  /* Create the file */"\
 42231     "  $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\
 42232     "  return $pHandle;"\
 42233     "}"\
 42234     "/* Creates a temporary filename */"\
 42235     "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
 42236     "{"\
 42237     "   return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
 42238     "}"\
 42239  	"function max(){"\
 42240      "  $pArgs = func_get_args();"\
 42241      " if( sizeof($pArgs) < 1 ){"\
 42242  	"  return null;"\
 42243      " }"\
 42244      " if( sizeof($pArgs) < 2 ){"\
 42245      " $pArg = $pArgs[0];"\
 42246  	" if( !is_array($pArg) ){"\
 42247  	"   return $pArg; "\
 42248  	" }"\
 42249  	" if( sizeof($pArg) < 1 ){"\
 42250  	"   return null;"\
 42251  	" }"\
 42252  	" $pArg = array_copy($pArgs[0]);"\
 42253  	" reset($pArg);"\
 42254  	" $max = current($pArg);"\
 42255  	" while( FALSE !== ($val = next($pArg)) ){"\
 42256  	"   if( $val > $max ){"\
 42257  	"     $max = $val;"\
 42258      " }"\
 42259  	" }"\
 42260  	" return $max;"\
 42261      " }"\
 42262      " $max = $pArgs[0];"\
 42263      " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
 42264      " $val = $pArgs[$i];"\
 42265  	"if( $val > $max ){"\
 42266  	" $max = $val;"\
 42267  	"}"\
 42268      " }"\
 42269  	" return $max;"\
 42270      "}"\
 42271  	"function min(){"\
 42272      "  $pArgs = func_get_args();"\
 42273      " if( sizeof($pArgs) < 1 ){"\
 42274  	"  return null;"\
 42275      " }"\
 42276      " if( sizeof($pArgs) < 2 ){"\
 42277      " $pArg = $pArgs[0];"\
 42278  	" if( !is_array($pArg) ){"\
 42279  	"   return $pArg; "\
 42280  	" }"\
 42281  	" if( sizeof($pArg) < 1 ){"\
 42282  	"   return null;"\
 42283  	" }"\
 42284  	" $pArg = array_copy($pArgs[0]);"\
 42285  	" reset($pArg);"\
 42286  	" $min = current($pArg);"\
 42287  	" while( FALSE !== ($val = next($pArg)) ){"\
 42288  	"   if( $val < $min ){"\
 42289  	"     $min = $val;"\
 42290      " }"\
 42291  	" }"\
 42292  	" return $min;"\
 42293      " }"\
 42294      " $min = $pArgs[0];"\
 42295      " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
 42296      " $val = $pArgs[$i];"\
 42297  	"if( $val < $min ){"\
 42298  	" $min = $val;"\
 42299  	" }"\
 42300      " }"\
 42301  	" return $min;"\
 42302  	"}"
 42303  /*
 42304   * Initialize a freshly allocated JX9 Virtual Machine so that we can
 42305   * start compiling the target JX9 program.
 42306   */
 42307  JX9_PRIVATE sxi32 jx9VmInit(
 42308  	 jx9_vm *pVm, /* Initialize this */
 42309  	 jx9 *pEngine /* Master engine */
 42310  	 )
 42311  {
 42312  	SyString sBuiltin;
 42313  	jx9_value *pObj;
 42314  	sxi32 rc;
 42315  	/* Zero the structure */
 42316  	SyZero(pVm, sizeof(jx9_vm));
 42317  	/* Initialize VM fields */
 42318  	pVm->pEngine = &(*pEngine);
 42319  	SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator);
 42320  	/* Instructions containers */
 42321  	SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
 42322  	SySetAlloc(&pVm->aByteCode, 0xFF);
 42323  	pVm->pByteContainer = &pVm->aByteCode;
 42324  	/* Object containers */
 42325  	SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value));
 42326  	SySetAlloc(&pVm->aMemObj, 0xFF);
 42327  	/* Virtual machine internal containers */
 42328  	SyBlobInit(&pVm->sConsumer, &pVm->sAllocator);
 42329  	SyBlobInit(&pVm->sWorker, &pVm->sAllocator);
 42330  	SyBlobInit(&pVm->sArgv, &pVm->sAllocator);
 42331  	SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value));
 42332  	SySetAlloc(&pVm->aLitObj, 0xFF);
 42333  	SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
 42334  	SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
 42335  	SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
 42336  	SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
 42337  	SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
 42338  	/* Configuration containers */
 42339  	SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
 42340  	SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
 42341  	SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
 42342  	SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
 42343  	/* Error callbacks containers */
 42344  	jx9MemObjInit(&(*pVm), &pVm->sAssertCallback);
 42345  	/* Set a default recursion limit */
 42346  #if defined(__WINNT__) || defined(__UNIXES__)
 42347  	pVm->nMaxDepth = 32;
 42348  #else
 42349  	pVm->nMaxDepth = 16;
 42350  #endif
 42351  	/* Default assertion flags */
 42352  	pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
 42353  	/* PRNG context */
 42354  	SyRandomnessInit(&pVm->sPrng, 0, 0);
 42355  	/* Install the null constant */
 42356  	pObj = jx9VmReserveConstObj(&(*pVm), 0);
 42357  	if( pObj == 0 ){
 42358  		rc = SXERR_MEM;
 42359  		goto Err;
 42360  	}
 42361  	jx9MemObjInit(pVm, pObj);
 42362  	/* Install the boolean TRUE constant */
 42363  	pObj = jx9VmReserveConstObj(&(*pVm), 0);
 42364  	if( pObj == 0 ){
 42365  		rc = SXERR_MEM;
 42366  		goto Err;
 42367  	}
 42368  	jx9MemObjInitFromBool(pVm, pObj, 1);
 42369  	/* Install the boolean FALSE constant */
 42370  	pObj = jx9VmReserveConstObj(&(*pVm), 0);
 42371  	if( pObj == 0 ){
 42372  		rc = SXERR_MEM;
 42373  		goto Err;
 42374  	}
 42375  	jx9MemObjInitFromBool(pVm, pObj, 0);
 42376  	/* Create the global frame */
 42377  	rc = VmEnterFrame(&(*pVm), 0, 0);
 42378  	if( rc != SXRET_OK ){
 42379  		goto Err;
 42380  	}
 42381  	/* Initialize the code generator */
 42382  	rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
 42383  	if( rc != SXRET_OK ){
 42384  		goto Err;
 42385  	}
 42386  	/* VM correctly initialized, set the magic number */
 42387  	pVm->nMagic = JX9_VM_INIT;
 42388  	SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
 42389  	/* Compile the built-in library */
 42390  	VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
 42391  	/* Reset the code generator */
 42392  	jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData);
 42393  	return SXRET_OK;
 42394  Err:
 42395  	SyMemBackendRelease(&pVm->sAllocator);
 42396  	return rc;
 42397  }
 42398  /*
 42399   * Default VM output consumer callback.That is, all VM output is redirected to this
 42400   * routine which store the output in an internal blob.
 42401   * The output can be extracted later after program execution [jx9_vm_exec()] via
 42402   * the [jx9_vm_config()] interface with a configuration verb set to
 42403   * jx9VM_CONFIG_EXTRACT_OUTPUT.
 42404   * Refer to the official docurmentation for additional information.
 42405   * Note that for performance reason it's preferable to install a VM output
 42406   * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
 42407   * to finish executing and extracting the output.
 42408   */
 42409  JX9_PRIVATE sxi32 jx9VmBlobConsumer(
 42410  	const void *pOut,   /* VM Generated output*/
 42411  	unsigned int nLen,  /* Generated output length */
 42412  	void *pUserData     /* User private data */
 42413  	)
 42414  {
 42415  	 sxi32 rc;
 42416  	 /* Store the output in an internal BLOB */
 42417  	 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
 42418  	 return rc;
 42419  }
 42420  #define VM_STACK_GUARD 16
 42421  /*
 42422   * Allocate a new operand stack so that we can start executing
 42423   * our compiled JX9 program.
 42424   * Return a pointer to the operand stack (array of jx9_values)
 42425   * on success. NULL (Fatal error) on failure.
 42426   */
 42427  static jx9_value * VmNewOperandStack(
 42428  	jx9_vm *pVm, /* Target VM */
 42429  	sxu32 nInstr /* Total numer of generated bytecode instructions */
 42430  	)
 42431  {
 42432  	jx9_value *pStack;
 42433    /* No instruction ever pushes more than a single element onto the
 42434    ** stack and the stack never grows on successive executions of the
 42435    ** same loop. So the total number of instructions is an upper bound
 42436    ** on the maximum stack depth required.
 42437    **
 42438    ** Allocation all the stack space we will ever need.
 42439    */
 42440  	nInstr += VM_STACK_GUARD;
 42441  	pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
 42442  	if( pStack == 0 ){
 42443  		return 0;
 42444  	}
 42445  	/* Initialize the operand stack */
 42446  	while( nInstr > 0 ){
 42447  		jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
 42448  		--nInstr;
 42449  	}
 42450  	/* Ready for bytecode execution */
 42451  	return pStack;
 42452  }
 42453  /* Forward declaration */
 42454  static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
 42455  /*
 42456   * Prepare the Virtual Machine for bytecode execution.
 42457   * This routine gets called by the JX9 engine after
 42458   * successful compilation of the target JX9 program.
 42459   */
 42460  JX9_PRIVATE sxi32 jx9VmMakeReady(
 42461  	jx9_vm *pVm /* Target VM */
 42462  	)
 42463  {
 42464  	sxi32 rc;
 42465  	if( pVm->nMagic != JX9_VM_INIT ){
 42466  		/* Initialize your VM first */
 42467  		return SXERR_CORRUPT;
 42468  	}
 42469  	/* Mark the VM ready for bytecode execution */
 42470  	pVm->nMagic = JX9_VM_RUN; 
 42471  	/* Release the code generator now we have compiled our program */
 42472  	jx9ResetCodeGenerator(pVm, 0, 0);
 42473  	/* Emit the DONE instruction */
 42474  	rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
 42475  	if( rc != SXRET_OK ){
 42476  		return SXERR_MEM;
 42477  	}
 42478  	/* Script return value */
 42479  	jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */
 42480  	/* Allocate a new operand stack */	
 42481  	pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer));
 42482  	if( pVm->aOps == 0 ){
 42483  		return SXERR_MEM;
 42484  	}
 42485  	/* Set the default VM output consumer callback and it's 
 42486  	 * private data. */
 42487  	pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer;
 42488  	pVm->sVmConsumer.pUserData = &pVm->sConsumer;
 42489  	/* Register special functions first [i.e: print, func_get_args(), die, etc.] */
 42490  	rc = VmRegisterSpecialFunction(&(*pVm));
 42491  	if( rc != SXRET_OK ){
 42492  		/* Don't worry about freeing memory, everything will be released shortly */
 42493  		return rc;
 42494  	}
 42495  	/* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */
 42496  	rc = jx9HashmapLoadBuiltin(&(*pVm));
 42497  	if( rc != SXRET_OK ){
 42498  		/* Don't worry about freeing memory, everything will be released shortly */
 42499  		return rc;
 42500  	}
 42501  	/* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
 42502  	jx9RegisterBuiltInConstant(&(*pVm));
 42503  	/* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
 42504  	jx9RegisterBuiltInFunction(&(*pVm));
 42505  	/* VM is ready for bytecode execution */
 42506  	return SXRET_OK;
 42507  }
 42508  /*
 42509   * Reset a Virtual Machine to it's initial state.
 42510   */
 42511  JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
 42512  {
 42513  	if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
 42514  		return SXERR_CORRUPT;
 42515  	}
 42516  	/* TICKET 1433-003: As of this version, the VM is automatically reset */
 42517  	SyBlobReset(&pVm->sConsumer);
 42518  	jx9MemObjRelease(&pVm->sExec);
 42519  	/* Set the ready flag */
 42520  	pVm->nMagic = JX9_VM_RUN;
 42521  	return SXRET_OK;
 42522  }
 42523  /*
 42524   * Release a Virtual Machine.
 42525   * Every virtual machine must be destroyed in order to avoid memory leaks.
 42526   */
 42527  JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
 42528  {
 42529  	/* Set the stale magic number */
 42530  	pVm->nMagic = JX9_VM_STALE;
 42531  	/* Release the private memory subsystem */
 42532  	SyMemBackendRelease(&pVm->sAllocator);
 42533  	return SXRET_OK;
 42534  }
 42535  /*
 42536   * Initialize a foreign function call context.
 42537   * The context in which a foreign function executes is stored in a jx9_context object.
 42538   * A pointer to a jx9_context object is always first parameter to application-defined foreign
 42539   * functions.
 42540   * The application-defined foreign function implementation will pass this pointer through into
 42541   * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(), 
 42542   * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
 42543   * and many more. Refer to the C/C++ Interfaces documentation for additional information.
 42544   */
 42545  static sxi32 VmInitCallContext(
 42546  	jx9_context *pOut,    /* Call Context */
 42547  	jx9_vm *pVm,          /* Target VM */
 42548  	jx9_user_func *pFunc, /* Foreign function to execute shortly */
 42549  	jx9_value *pRet,      /* Store return value here*/
 42550  	sxi32 iFlags          /* Control flags */
 42551  	)
 42552  {
 42553  	pOut->pFunc = pFunc;
 42554  	pOut->pVm   = pVm;
 42555  	SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
 42556  	SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data));
 42557  	/* Assume a null return value */
 42558  	MemObjSetType(pRet, MEMOBJ_NULL);
 42559  	pOut->pRet = pRet;
 42560  	pOut->iFlags = iFlags;
 42561  	return SXRET_OK;
 42562  }
 42563  /*
 42564   * Release a foreign function call context and cleanup the mess
 42565   * left behind.
 42566   */
 42567  static void VmReleaseCallContext(jx9_context *pCtx)
 42568  {
 42569  	sxu32 n;
 42570  	if( SySetUsed(&pCtx->sVar) > 0 ){
 42571  		jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
 42572  		for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
 42573  			if( apObj[n] == 0 ){
 42574  				/* Already released */
 42575  				continue;
 42576  			}
 42577  			jx9MemObjRelease(apObj[n]);
 42578  			SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
 42579  		}
 42580  		SySetRelease(&pCtx->sVar);
 42581  	}
 42582  	if( SySetUsed(&pCtx->sChunk) > 0 ){
 42583  		jx9_aux_data *aAux;
 42584  		void *pChunk;
 42585  		/* Automatic release of dynamically allocated chunk 
 42586  		 * using [jx9_context_alloc_chunk()].
 42587  		 */
 42588  		aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
 42589  		for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
 42590  			pChunk = aAux[n].pAuxData;
 42591  			/* Release the chunk */
 42592  			if( pChunk ){
 42593  				SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
 42594  			}
 42595  		}
 42596  		SySetRelease(&pCtx->sChunk);
 42597  	}
 42598  }
 42599  /*
 42600   * Release a jx9_value allocated from the body of a foreign function.
 42601   * Refer to [jx9_context_release_value()] for additional information.
 42602   */
 42603  JX9_PRIVATE void jx9VmReleaseContextValue(
 42604  	jx9_context *pCtx, /* Call context */
 42605  	jx9_value *pValue  /* Release this value */
 42606  	)
 42607  {
 42608  	if( pValue == 0 ){
 42609  		/* NULL value is a harmless operation */
 42610  		return;
 42611  	}
 42612  	if( SySetUsed(&pCtx->sVar) > 0 ){
 42613  		jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
 42614  		sxu32 n;
 42615  		for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
 42616  			if( apObj[n] == pValue ){
 42617  				jx9MemObjRelease(pValue);
 42618  				SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue);
 42619  				/* Mark as released */
 42620  				apObj[n] = 0;
 42621  				break;
 42622  			}
 42623  		}
 42624  	}
 42625  }
 42626  /*
 42627   * Pop and release as many memory object from the operand stack.
 42628   */
 42629  static void VmPopOperand(
 42630  	jx9_value **ppTos, /* Operand stack */
 42631  	sxi32 nPop         /* Total number of memory objects to pop */
 42632  	)
 42633  {
 42634  	jx9_value *pTos = *ppTos;
 42635  	while( nPop > 0 ){
 42636  		jx9MemObjRelease(pTos);
 42637  		pTos--;
 42638  		nPop--;
 42639  	}
 42640  	/* Top of the stack */
 42641  	*ppTos = pTos;
 42642  }
 42643  /*
 42644   * Reserve a memory object.
 42645   * Return a pointer to the raw jx9_value on success. NULL on failure.
 42646   */
 42647  JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
 42648  {
 42649  	jx9_value *pObj = 0;
 42650  	VmSlot *pSlot;
 42651  	sxu32 nIdx;
 42652  	/* Check for a free slot */
 42653  	nIdx = SXU32_HIGH; /* cc warning */
 42654  	pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj);
 42655  	if( pSlot ){
 42656  		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx);
 42657  		nIdx = pSlot->nIdx;
 42658  	}
 42659  	if( pObj == 0 ){
 42660  		/* Reserve a new memory object */
 42661  		pObj = VmReserveMemObj(&(*pVm), &nIdx);
 42662  		if( pObj == 0 ){
 42663  			return 0;
 42664  		}
 42665  	}
 42666  	/* Set a null default value */
 42667  	jx9MemObjInit(&(*pVm), pObj);
 42668  	if( pIdx ){
 42669  		*pIdx = nIdx;
 42670  	}
 42671  	pObj->nIdx = nIdx;
 42672  	return pObj;
 42673  }
 42674  /*
 42675   * Extract a variable value from the top active VM frame.
 42676   * Return a pointer to the variable value on success. 
 42677   * NULL otherwise (non-existent variable/Out-of-memory, ...).
 42678   */
 42679  static jx9_value * VmExtractMemObj(
 42680  	jx9_vm *pVm,           /* Target VM */
 42681  	const SyString *pName, /* Variable name */
 42682  	int bDup,              /* True to duplicate variable name */
 42683  	int bCreate            /* True to create the variable if non-existent */
 42684  	)
 42685  {
 42686  	int bNullify = FALSE;
 42687  	SyHashEntry *pEntry;
 42688  	VmFrame *pFrame;
 42689  	jx9_value *pObj;
 42690  	sxu32 nIdx;
 42691  	sxi32 rc;
 42692  	/* Point to the top active frame */
 42693  	pFrame = pVm->pFrame;
 42694  	/* Perform the lookup */
 42695  	if( pName == 0 || pName->nByte < 1 ){
 42696  		static const SyString sAnnon = { " " , sizeof(char) };
 42697  		pName = &sAnnon;
 42698  		/* Always nullify the object */
 42699  		bNullify = TRUE;
 42700  		bDup = FALSE;
 42701  	}
 42702  	/* Check the superglobals table first */
 42703  	pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte);
 42704  	if( pEntry == 0 ){
 42705  		/* Query the top active frame */
 42706  		pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
 42707  		if( pEntry == 0 ){
 42708  			char *zName = (char *)pName->zString;
 42709  			VmSlot sLocal;
 42710  			if( !bCreate ){
 42711  				/* Do not create the variable, return NULL */
 42712  				return 0;
 42713  			}
 42714  			/* No such variable, automatically create a new one and install
 42715  			 * it in the current frame.
 42716  			 */
 42717  			pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
 42718  			if( pObj == 0 ){
 42719  				return 0;
 42720  			}
 42721  			if( bDup ){
 42722  				/* Duplicate name */
 42723  				zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
 42724  				if( zName == 0 ){
 42725  					return 0;
 42726  				}
 42727  			}
 42728  			/* Link to the top active VM frame */
 42729  			rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx));
 42730  			if( rc != SXRET_OK ){
 42731  				/* Return the slot to the free pool */
 42732  				sLocal.nIdx = nIdx;
 42733  				sLocal.pUserData = 0;
 42734  				SySetPut(&pVm->aFreeObj, (const void *)&sLocal);
 42735  				return 0;
 42736  			}
 42737  			if( pFrame->pParent != 0 ){
 42738  				/* Local variable */
 42739  				sLocal.nIdx = nIdx;
 42740  				SySetPut(&pFrame->sLocal, (const void *)&sLocal);
 42741  			}
 42742  		}else{
 42743  			/* Extract variable contents */
 42744  			nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
 42745  			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 42746  			if( bNullify && pObj ){
 42747  				jx9MemObjRelease(pObj);
 42748  			}
 42749  		}
 42750  	}else{
 42751  		/* Superglobal */
 42752  		nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
 42753  		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 42754  	}
 42755  	return pObj;
 42756  }
 42757  /*
 42758   * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, .... 
 42759   * Return a pointer to the variable value on success.NULL otherwise.
 42760   */
 42761  static jx9_value * VmExtractSuper(
 42762  	jx9_vm *pVm,       /* Target VM */
 42763  	const char *zName, /* Superglobal name: NOT NULL TERMINATED */
 42764  	sxu32 nByte        /* zName length */
 42765  	)
 42766  {
 42767  	SyHashEntry *pEntry;
 42768  	jx9_value *pValue;
 42769  	sxu32 nIdx;
 42770  	/* Query the superglobal table */
 42771  	pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
 42772  	if( pEntry == 0 ){
 42773  		/* No such entry */
 42774  		return 0;
 42775  	}
 42776  	/* Extract the superglobal index in the global object pool */
 42777  	nIdx = SX_PTR_TO_INT(pEntry->pUserData);
 42778  	/* Extract the variable value  */
 42779  	pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 42780  	return pValue;
 42781  }
 42782  /*
 42783   * Perform a raw hashmap insertion.
 42784   * Refer to the [jx9VmConfigure()] implementation for additional information.
 42785   */
 42786  static sxi32 VmHashmapInsert(
 42787  	jx9_hashmap *pMap,  /* Target hashmap  */
 42788  	const char *zKey,   /* Entry key */
 42789  	int nKeylen,        /* zKey length*/
 42790  	const char *zData,  /* Entry data */
 42791  	int nLen            /* zData length */
 42792  	)
 42793  {
 42794  	jx9_value sKey,sValue;
 42795  	jx9_value *pKey;
 42796  	sxi32 rc;
 42797  	pKey = 0;
 42798  	jx9MemObjInit(pMap->pVm, &sKey);
 42799  	jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
 42800  	if( zKey ){
 42801  		if( nKeylen < 0 ){
 42802  			nKeylen = (int)SyStrlen(zKey);
 42803  		}
 42804  		jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
 42805  		pKey = &sKey;
 42806  	}
 42807  	if( zData ){
 42808  		if( nLen < 0 ){
 42809  			/* Compute length automatically */
 42810  			nLen = (int)SyStrlen(zData);
 42811  		}
 42812  		jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
 42813  	}
 42814  	/* Perform the insertion */
 42815  	rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
 42816  	jx9MemObjRelease(&sKey);
 42817  	jx9MemObjRelease(&sValue);
 42818  	return rc;
 42819  }
 42820  /* Forward declaration */
 42821  static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
 42822  /*
 42823   * Configure a working virtual machine instance.
 42824   *
 42825   * This routine is used to configure a JX9 virtual machine obtained by a prior
 42826   * successful call to one of the compile interface such as jx9_compile()
 42827   * jx9_compile_v2() or jx9_compile_file().
 42828   * The second argument to this function is an integer configuration option
 42829   * that determines what property of the JX9 virtual machine is to be configured.
 42830   * Subsequent arguments vary depending on the configuration option in the second
 42831   * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT, 
 42832   * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
 42833   * Refer to the official documentation for the list of allowed verbs.
 42834   */
 42835  JX9_PRIVATE sxi32 jx9VmConfigure(
 42836  	jx9_vm *pVm, /* Target VM */
 42837  	sxi32 nOp,   /* Configuration verb */
 42838  	va_list ap   /* Subsequent option arguments */
 42839  	)
 42840  {
 42841  	sxi32 rc = SXRET_OK;
 42842  	switch(nOp){
 42843  	case JX9_VM_CONFIG_OUTPUT: {
 42844  		ProcConsumer xConsumer = va_arg(ap, ProcConsumer);
 42845  		void *pUserData = va_arg(ap, void *);
 42846  		/* VM output consumer callback */
 42847  #ifdef UNTRUST
 42848  		if( xConsumer == 0 ){
 42849  			rc = SXERR_CORRUPT;
 42850  			break;
 42851  		}
 42852  #endif
 42853  		/* Install the output consumer */
 42854  		pVm->sVmConsumer.xConsumer = xConsumer;
 42855  		pVm->sVmConsumer.pUserData = pUserData;
 42856  		break;
 42857  							   }
 42858  	case JX9_VM_CONFIG_IMPORT_PATH: {
 42859  		/* Import path */
 42860  		  const char *zPath;
 42861  		  SyString sPath;
 42862  		  zPath = va_arg(ap, const char *);
 42863  #if defined(UNTRUST)
 42864  		  if( zPath == 0 ){
 42865  			  rc = SXERR_EMPTY;
 42866  			  break;
 42867  		  }
 42868  #endif
 42869  		  SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath));
 42870  		  /* Remove trailing slashes and backslashes */
 42871  #ifdef __WINNT__
 42872  		  SyStringTrimTrailingChar(&sPath, '\\');
 42873  #endif
 42874  		  SyStringTrimTrailingChar(&sPath, '/');
 42875  		  /* Remove leading and trailing white spaces */
 42876  		  SyStringFullTrim(&sPath);
 42877  		  if( sPath.nByte > 0 ){
 42878  			  /* Store the path in the corresponding conatiner */
 42879  			  rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
 42880  		  }
 42881  		  break;
 42882  									 }
 42883  	case JX9_VM_CONFIG_ERR_REPORT:
 42884  		/* Run-Time Error report */
 42885  		pVm->bErrReport = 1;
 42886  		break;
 42887  	case JX9_VM_CONFIG_RECURSION_DEPTH:{
 42888  		/* Recursion depth */
 42889  		int nDepth = va_arg(ap, int);
 42890  		if( nDepth > 2 && nDepth < 1024 ){
 42891  			pVm->nMaxDepth = nDepth;
 42892  		}
 42893  		break;
 42894  									   }
 42895  	case JX9_VM_OUTPUT_LENGTH: {
 42896  		/* VM output length in bytes */
 42897  		sxu32 *pOut = va_arg(ap, sxu32 *);
 42898  #ifdef UNTRUST
 42899  		if( pOut == 0 ){
 42900  			rc = SXERR_CORRUPT;
 42901  			break;
 42902  		}
 42903  #endif
 42904  		*pOut = pVm->nOutputLen;
 42905  		break;
 42906  							   }
 42907  	case JX9_VM_CONFIG_CREATE_VAR: {
 42908  		/* Create a new superglobal/global variable */
 42909  		const char *zName = va_arg(ap, const char *);
 42910  		jx9_value *pValue = va_arg(ap, jx9_value *);
 42911  		SyHashEntry *pEntry;
 42912  		jx9_value *pObj;
 42913  		sxu32 nByte;
 42914  		sxu32 nIdx; 
 42915  #ifdef UNTRUST
 42916  		if( SX_EMPTY_STR(zName) || pValue == 0 ){
 42917  			rc = SXERR_CORRUPT;
 42918  			break;
 42919  		}
 42920  #endif
 42921  		nByte = SyStrlen(zName);
 42922  		/* Check if the superglobal is already installed */
 42923  		pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
 42924  		if( pEntry ){
 42925  			/* Variable already installed */
 42926  			nIdx = SX_PTR_TO_INT(pEntry->pUserData);
 42927  			/* Extract contents */
 42928  			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 42929  			if( pObj ){
 42930  				/* Overwrite old contents */
 42931  				jx9MemObjStore(pValue, pObj);
 42932  			}
 42933  		}else{
 42934  			/* Install a new variable */
 42935  			pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
 42936  			if( pObj == 0 ){
 42937  				rc = SXERR_MEM;
 42938  				break;
 42939  			}
 42940  			/* Copy value */
 42941  			jx9MemObjStore(pValue, pObj);
 42942  			/* Install the superglobal */
 42943  			rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
 42944  		}
 42945  		break;
 42946  									}
 42947  	case JX9_VM_CONFIG_SERVER_ATTR:
 42948  	case JX9_VM_CONFIG_ENV_ATTR:  {
 42949  		const char *zKey   = va_arg(ap, const char *);
 42950  		const char *zValue = va_arg(ap, const char *);
 42951  		int nLen = va_arg(ap, int);
 42952  		jx9_hashmap *pMap;
 42953  		jx9_value *pValue;
 42954  		if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
 42955  			/* Extract the $_ENV superglobal */
 42956  			pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1);
 42957  		}else{
 42958  			/* Extract the $_SERVER superglobal */
 42959  			pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1);
 42960  		}
 42961  		if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
 42962  			/* No such entry */
 42963  			rc = SXERR_NOTFOUND;
 42964  			break;
 42965  		}
 42966  		/* Point to the hashmap */
 42967  		pMap = (jx9_hashmap *)pValue->x.pOther;
 42968  		/* Perform the insertion */
 42969  		rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
 42970  		break;
 42971  								   }
 42972  	case JX9_VM_CONFIG_ARGV_ENTRY:{
 42973  		/* Script arguments */
 42974  		const char *zValue = va_arg(ap, const char *);
 42975  		jx9_hashmap *pMap;
 42976  		jx9_value *pValue;
 42977  		/* Extract the $argv array */
 42978  		pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1);
 42979  		if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
 42980  			/* No such entry */
 42981  			rc = SXERR_NOTFOUND;
 42982  			break;
 42983  		}
 42984  		/* Point to the hashmap */
 42985  		pMap = (jx9_hashmap *)pValue->x.pOther;
 42986  		/* Perform the insertion */
 42987  		rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
 42988  		if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
 42989  			if( pMap->nEntry > 1 ){
 42990  				/* Append space separator first */
 42991  				SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
 42992  			}
 42993  			SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
 42994  		}
 42995  		break;
 42996  								  }
 42997  	case JX9_VM_CONFIG_EXEC_VALUE: {
 42998  		/* Script return value */
 42999  		jx9_value **ppValue = va_arg(ap, jx9_value **);
 43000  #ifdef UNTRUST
 43001  		if( ppValue == 0 ){
 43002  			rc = SXERR_CORRUPT;
 43003  			break;
 43004  		}
 43005  #endif
 43006  		*ppValue = &pVm->sExec;
 43007  		break;
 43008  								   }
 43009  	case JX9_VM_CONFIG_IO_STREAM: {
 43010  		/* Register an IO stream device */
 43011  		const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *);
 43012  		/* Make sure we are dealing with a valid IO stream */
 43013  		if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 ||
 43014  			pStream->xOpen == 0 || pStream->xRead == 0 ){
 43015  				/* Invalid stream */
 43016  				rc = SXERR_INVALID;
 43017  				break;
 43018  		}
 43019  		if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){
 43020  			/* Make the 'file://' stream the defaut stream device */
 43021  			pVm->pDefStream = pStream;
 43022  		}
 43023  		/* Insert in the appropriate container */
 43024  		rc = SySetPut(&pVm->aIOstream, (const void *)&pStream);
 43025  		break;
 43026  								  }
 43027  	case JX9_VM_CONFIG_EXTRACT_OUTPUT: {
 43028  		/* Point to the VM internal output consumer buffer */
 43029  		const void **ppOut = va_arg(ap, const void **);
 43030  		unsigned int *pLen = va_arg(ap, unsigned int *);
 43031  #ifdef UNTRUST
 43032  		if( ppOut == 0 || pLen == 0 ){
 43033  			rc = SXERR_CORRUPT;
 43034  			break;
 43035  		}
 43036  #endif
 43037  		*ppOut = SyBlobData(&pVm->sConsumer);
 43038  		*pLen  = SyBlobLength(&pVm->sConsumer);
 43039  		break;
 43040  									   }
 43041  	case JX9_VM_CONFIG_HTTP_REQUEST:{
 43042  		/* Raw HTTP request*/
 43043  		const char *zRequest = va_arg(ap, const char *);
 43044  		int nByte = va_arg(ap, int);
 43045  		if( SX_EMPTY_STR(zRequest) ){
 43046  			rc = SXERR_EMPTY;
 43047  			break;
 43048  		}
 43049  		if( nByte < 0 ){
 43050  			/* Compute length automatically */
 43051  			nByte = (int)SyStrlen(zRequest);
 43052  		}
 43053  		/* Process the request */
 43054  		rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte);
 43055  		break;
 43056  									}
 43057  	default:
 43058  		/* Unknown configuration option */
 43059  		rc = SXERR_UNKNOWN;
 43060  		break;
 43061  	}
 43062  	return rc;
 43063  }
 43064  /* Forward declaration */
 43065  static const char * VmInstrToString(sxi32 nOp);
 43066  /*
 43067   * This routine is used to dump JX9 bytecode instructions to a human readable
 43068   * format.
 43069   * The dump is redirected to the given consumer callback which is responsible
 43070   * of consuming the generated dump perhaps redirecting it to its standard output
 43071   * (STDOUT).
 43072   */
 43073  static sxi32 VmByteCodeDump(
 43074  	SySet *pByteCode,       /* Bytecode container */
 43075  	ProcConsumer xConsumer, /* Dump consumer callback */
 43076  	void *pUserData         /* Last argument to xConsumer() */
 43077  	)
 43078  {
 43079  	static const char zDump[] = {
 43080  		"====================================================\n"
 43081  		"JX9 VM Dump   Copyright (C) 2012-2013 Symisc Systems\n"
 43082  		"                              http://jx9.symisc.net/\n"
 43083  		"====================================================\n"
 43084  	};
 43085  	VmInstr *pInstr, *pEnd;
 43086  	sxi32 rc = SXRET_OK;
 43087  	sxu32 n;
 43088  	/* Point to the JX9 instructions */
 43089  	pInstr = (VmInstr *)SySetBasePtr(pByteCode);
 43090  	pEnd   = &pInstr[SySetUsed(pByteCode)];
 43091  	n = 0;
 43092  	xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData);
 43093  	/* Dump instructions */
 43094  	for(;;){
 43095  		if( pInstr >= pEnd ){
 43096  			/* No more instructions */
 43097  			break;
 43098  		}
 43099  		/* Format and call the consumer callback */
 43100  		rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n", 
 43101  			VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2, 
 43102  			SX_PTR_TO_INT(pInstr->p3), n);
 43103  		if( rc != SXRET_OK ){
 43104  			/* Consumer routine request an operation abort */
 43105  			return rc;
 43106  		}
 43107  		++n;
 43108  		pInstr++; /* Next instruction in the stream */
 43109  	}
 43110  	return rc;
 43111  }
 43112  /*
 43113   * Consume a generated run-time error message by invoking the VM output
 43114   * consumer callback.
 43115   */
 43116  static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
 43117  {
 43118  	jx9_output_consumer *pCons = &pVm->sVmConsumer;
 43119  	sxi32 rc = SXRET_OK;
 43120  	/* Append a new line */
 43121  #ifdef __WINNT__
 43122  	SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1);
 43123  #else
 43124  	SyBlobAppend(pMsg, "\n", sizeof(char));
 43125  #endif
 43126  	/* Invoke the output consumer callback */
 43127  	rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData);
 43128  	/* Increment output length */
 43129  	pVm->nOutputLen += SyBlobLength(pMsg);
 43130  	
 43131  	return rc;
 43132  }
 43133  /*
 43134   * Throw a run-time error and invoke the supplied VM output consumer callback.
 43135   * Refer to the implementation of [jx9_context_throw_error()] for additional
 43136   * information.
 43137   */
 43138  JX9_PRIVATE sxi32 jx9VmThrowError(
 43139  	jx9_vm *pVm,         /* Target VM */
 43140  	SyString *pFuncName, /* Function name. NULL otherwise */
 43141  	sxi32 iErr,          /* Severity level: [i.e: Error, Warning or Notice]*/
 43142  	const char *zMessage /* Null terminated error message */
 43143  	)
 43144  {
 43145  	SyBlob *pWorker = &pVm->sWorker;
 43146  	SyString *pFile;
 43147  	char *zErr;
 43148  	sxi32 rc;
 43149  	if( !pVm->bErrReport ){
 43150  		/* Don't bother reporting errors */
 43151  		return SXRET_OK;
 43152  	}
 43153  	/* Reset the working buffer */
 43154  	SyBlobReset(pWorker);
 43155  	/* Peek the processed file if available */
 43156  	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 43157  	if( pFile ){
 43158  		/* Append file name */
 43159  		SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
 43160  		SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
 43161  	}
 43162  	zErr = "Error: ";
 43163  	switch(iErr){
 43164  	case JX9_CTX_WARNING: zErr = "Warning: "; break;
 43165  	case JX9_CTX_NOTICE:  zErr = "Notice: ";  break;
 43166  	default:
 43167  		iErr = JX9_CTX_ERR;
 43168  		break;
 43169  	}
 43170  	SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
 43171  	if( pFuncName ){
 43172  		/* Append function name first */
 43173  		SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
 43174  		SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
 43175  	}
 43176  	SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage));
 43177  	/* Consume the error message */
 43178  	rc = VmCallErrorHandler(&(*pVm), pWorker);
 43179  	return rc;
 43180  }
 43181  /*
 43182   * Format and throw a run-time error and invoke the supplied VM output consumer callback.
 43183   * Refer to the implementation of [jx9_context_throw_error_format()] for additional
 43184   * information.
 43185   */
 43186  static sxi32 VmThrowErrorAp(
 43187  	jx9_vm *pVm,         /* Target VM */
 43188  	SyString *pFuncName, /* Function name. NULL otherwise */
 43189  	sxi32 iErr,          /* Severity level: [i.e: Error, Warning or Notice] */
 43190  	const char *zFormat, /* Format message */
 43191  	va_list ap           /* Variable list of arguments */
 43192  	)
 43193  {
 43194  	SyBlob *pWorker = &pVm->sWorker;
 43195  	SyString *pFile;
 43196  	char *zErr;
 43197  	sxi32 rc;
 43198  	if( !pVm->bErrReport ){
 43199  		/* Don't bother reporting errors */
 43200  		return SXRET_OK;
 43201  	}
 43202  	/* Reset the working buffer */
 43203  	SyBlobReset(pWorker);
 43204  	/* Peek the processed file if available */
 43205  	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 43206  	if( pFile ){
 43207  		/* Append file name */
 43208  		SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
 43209  		SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
 43210  	}
 43211  	zErr = "Error: ";
 43212  	switch(iErr){
 43213  	case JX9_CTX_WARNING: zErr = "Warning: "; break;
 43214  	case JX9_CTX_NOTICE:  zErr = "Notice: ";  break;
 43215  	default:
 43216  		iErr = JX9_CTX_ERR;
 43217  		break;
 43218  	}
 43219  	SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
 43220  	if( pFuncName ){
 43221  		/* Append function name first */
 43222  		SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
 43223  		SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
 43224  	}
 43225  	SyBlobFormatAp(pWorker, zFormat, ap);
 43226  	/* Consume the error message */
 43227  	rc = VmCallErrorHandler(&(*pVm), pWorker);
 43228  	return rc;
 43229  }
 43230  /*
 43231   * Format and throw a run-time error and invoke the supplied VM output consumer callback.
 43232   * Refer to the implementation of [jx9_context_throw_error_format()] for additional
 43233   * information.
 43234   * ------------------------------------
 43235   * Simple boring wrapper function.
 43236   * ------------------------------------
 43237   */
 43238  static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...)
 43239  {
 43240  	va_list ap;
 43241  	sxi32 rc;
 43242  	va_start(ap, zFormat);
 43243  	rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap);
 43244  	va_end(ap);
 43245  	return rc;
 43246  }
 43247  /*
 43248   * Format and throw a run-time error and invoke the supplied VM output consumer callback.
 43249   * Refer to the implementation of [jx9_context_throw_error_format()] for additional
 43250   * information.
 43251   * ------------------------------------
 43252   * Simple boring wrapper function.
 43253   * ------------------------------------
 43254   */
 43255  JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
 43256  {
 43257  	sxi32 rc;
 43258  	rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
 43259  	return rc;
 43260  }
 43261  /* Forward declaration */
 43262  static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
 43263  /*
 43264   * Execute as much of a JX9 bytecode program as we can then return.
 43265   *
 43266   * [jx9VmMakeReady()] must be called before this routine in order to
 43267   * close the program with a final OP_DONE and to set up the default
 43268   * consumer routines and other stuff. Refer to the implementation
 43269   * of [jx9VmMakeReady()] for additional information.
 43270   * If the installed VM output consumer callback ever returns JX9_ABORT
 43271   * then the program execution is halted.
 43272   * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
 43273   * should be used respectively to clean up the mess that was left behind
 43274   * or to reset the VM to it's initial state.
 43275   */
 43276  static sxi32 VmByteCodeExec(
 43277  	jx9_vm *pVm,         /* Target VM */
 43278  	VmInstr *aInstr,     /* JX9 bytecode program */
 43279  	jx9_value *pStack,   /* Operand stack */
 43280  	int nTos,            /* Top entry in the operand stack (usually -1) */
 43281  	jx9_value *pResult  /* Store program return value here. NULL otherwise */
 43282  	)
 43283  {
 43284  	VmInstr *pInstr;
 43285  	jx9_value *pTos;
 43286  	SySet aArg;
 43287  	sxi32 pc;
 43288  	sxi32 rc;
 43289  	/* Argument container */
 43290  	SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
 43291  	if( nTos < 0 ){
 43292  		pTos = &pStack[-1];
 43293  	}else{
 43294  		pTos = &pStack[nTos];
 43295  	}
 43296  	pc = 0;
 43297  	/* Execute as much as we can */
 43298  	for(;;){
 43299  		/* Fetch the instruction to execute */
 43300  		pInstr = &aInstr[pc];
 43301  		rc = SXRET_OK;
 43302  /*
 43303   * What follows here is a massive switch statement where each case implements a
 43304   * separate instruction in the virtual machine.  If we follow the usual
 43305   * indentation convention each case should be indented by 6 spaces.  But
 43306   * that is a lot of wasted space on the left margin.  So the code within
 43307   * the switch statement will break with convention and be flush-left.
 43308   */
 43309  		switch(pInstr->iOp){
 43310  /*
 43311   * DONE: P1 * * 
 43312   *
 43313   * Program execution completed: Clean up the mess left behind
 43314   * and return immediately.
 43315   */
 43316  case JX9_OP_DONE:
 43317  	if( pInstr->iP1 ){
 43318  #ifdef UNTRUST
 43319  		if( pTos < pStack ){
 43320  			goto Abort;
 43321  		}
 43322  #endif
 43323  		if( pResult ){
 43324  			/* Execution result */
 43325  			jx9MemObjStore(pTos, pResult);
 43326  		}		
 43327  		VmPopOperand(&pTos, 1);
 43328  	}
 43329  	goto Done;
 43330  /*
 43331   * HALT: P1 * *
 43332   *
 43333   * Program execution aborted: Clean up the mess left behind
 43334   * and abort immediately.
 43335   */
 43336  case JX9_OP_HALT:
 43337  	if( pInstr->iP1 ){
 43338  #ifdef UNTRUST
 43339  		if( pTos < pStack ){
 43340  			goto Abort;
 43341  		}
 43342  #endif
 43343  		if( pTos->iFlags & MEMOBJ_STRING ){
 43344  			if( SyBlobLength(&pTos->sBlob) > 0 ){
 43345  				/* Output the exit message */
 43346  				pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob), 
 43347  					pVm->sVmConsumer.pUserData);
 43348  					/* Increment output length */
 43349  					pVm->nOutputLen += SyBlobLength(&pTos->sBlob);
 43350  			}
 43351  		}else if(pTos->iFlags & MEMOBJ_INT ){
 43352  			/* Record exit status */
 43353  			pVm->iExitStatus = (sxi32)pTos->x.iVal;
 43354  		}
 43355  		VmPopOperand(&pTos, 1);
 43356  	}
 43357  	goto Abort;
 43358  /*
 43359   * JMP: * P2 *
 43360   *
 43361   * Unconditional jump: The next instruction executed will be 
 43362   * the one at index P2 from the beginning of the program.
 43363   */
 43364  case JX9_OP_JMP:
 43365  	pc = pInstr->iP2 - 1;
 43366  	break;
 43367  /*
 43368   * JZ: P1 P2 *
 43369   *
 43370   * Take the jump if the top value is zero (FALSE jump).Pop the top most
 43371   * entry in the stack if P1 is zero. 
 43372   */
 43373  case JX9_OP_JZ:
 43374  #ifdef UNTRUST
 43375  	if( pTos < pStack ){
 43376  		goto Abort;
 43377  	}
 43378  #endif
 43379  	/* Get a boolean value */
 43380  	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 43381  		jx9MemObjToBool(pTos);
 43382  	}
 43383  	if( !pTos->x.iVal ){
 43384  		/* Take the jump */
 43385  		pc = pInstr->iP2 - 1;
 43386  	}
 43387  	if( !pInstr->iP1 ){
 43388  		VmPopOperand(&pTos, 1);
 43389  	}
 43390  	break;
 43391  /*
 43392   * JNZ: P1 P2 *
 43393   *
 43394   * Take the jump if the top value is not zero (TRUE jump).Pop the top most
 43395   * entry in the stack if P1 is zero.
 43396   */
 43397  case JX9_OP_JNZ:
 43398  #ifdef UNTRUST
 43399  	if( pTos < pStack ){
 43400  		goto Abort;
 43401  	}
 43402  #endif
 43403  	/* Get a boolean value */
 43404  	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 43405  		jx9MemObjToBool(pTos);
 43406  	}
 43407  	if( pTos->x.iVal ){
 43408  		/* Take the jump */
 43409  		pc = pInstr->iP2 - 1;
 43410  	}
 43411  	if( !pInstr->iP1 ){
 43412  		VmPopOperand(&pTos, 1);
 43413  	}
 43414  	break;
 43415  /*
 43416   * NOOP: * * *
 43417   *
 43418   * Do nothing. This instruction is often useful as a jump
 43419   * destination.
 43420   */
 43421  case JX9_OP_NOOP:
 43422  	break;
 43423  /*
 43424   * POP: P1 * *
 43425   *
 43426   * Pop P1 elements from the operand stack.
 43427   */
 43428  case JX9_OP_POP: {
 43429  	sxi32 n = pInstr->iP1;
 43430  	if( &pTos[-n+1] < pStack ){
 43431  		/* TICKET 1433-51 Stack underflow must be handled at run-time */
 43432  		n = (sxi32)(pTos - pStack);
 43433  	}
 43434  	VmPopOperand(&pTos, n);
 43435  	break;
 43436  				 }
 43437  /*
 43438   * CVT_INT: * * *
 43439   *
 43440   * Force the top of the stack to be an integer.
 43441   */
 43442  case JX9_OP_CVT_INT:
 43443  #ifdef UNTRUST
 43444  	if( pTos < pStack ){
 43445  		goto Abort;
 43446  	}
 43447  #endif
 43448  	if((pTos->iFlags & MEMOBJ_INT) == 0 ){
 43449  		jx9MemObjToInteger(pTos);
 43450  	}
 43451  	/* Invalidate any prior representation */
 43452  	MemObjSetType(pTos, MEMOBJ_INT);
 43453  	break;
 43454  /*
 43455   * CVT_REAL: * * *
 43456   *
 43457   * Force the top of the stack to be a real.
 43458   */
 43459  case JX9_OP_CVT_REAL:
 43460  #ifdef UNTRUST
 43461  	if( pTos < pStack ){
 43462  		goto Abort;
 43463  	}
 43464  #endif
 43465  	if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
 43466  		jx9MemObjToReal(pTos);
 43467  	}
 43468  	/* Invalidate any prior representation */
 43469  	MemObjSetType(pTos, MEMOBJ_REAL);
 43470  	break;
 43471  /*
 43472   * CVT_STR: * * *
 43473   *
 43474   * Force the top of the stack to be a string.
 43475   */
 43476  case JX9_OP_CVT_STR:
 43477  #ifdef UNTRUST
 43478  	if( pTos < pStack ){
 43479  		goto Abort;
 43480  	}
 43481  #endif
 43482  	if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 43483  		jx9MemObjToString(pTos);
 43484  	}
 43485  	break;
 43486  /*
 43487   * CVT_BOOL: * * *
 43488   *
 43489   * Force the top of the stack to be a boolean.
 43490   */
 43491  case JX9_OP_CVT_BOOL:
 43492  #ifdef UNTRUST
 43493  	if( pTos < pStack ){
 43494  		goto Abort;
 43495  	}
 43496  #endif
 43497  	if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 43498  		jx9MemObjToBool(pTos);
 43499  	}
 43500  	break;
 43501  /*
 43502   * CVT_NULL: * * *
 43503   *
 43504   * Nullify the top of the stack.
 43505   */
 43506  case JX9_OP_CVT_NULL:
 43507  #ifdef UNTRUST
 43508  	if( pTos < pStack ){
 43509  		goto Abort;
 43510  	}
 43511  #endif
 43512  	jx9MemObjRelease(pTos);
 43513  	break;
 43514  /*
 43515   * CVT_NUMC: * * *
 43516   *
 43517   * Force the top of the stack to be a numeric type (integer, real or both).
 43518   */
 43519  case JX9_OP_CVT_NUMC:
 43520  #ifdef UNTRUST
 43521  	if( pTos < pStack ){
 43522  		goto Abort;
 43523  	}
 43524  #endif
 43525  	/* Force a numeric cast */
 43526  	jx9MemObjToNumeric(pTos);
 43527  	break;
 43528  /*
 43529   * CVT_ARRAY: * * *
 43530   *
 43531   * Force the top of the stack to be a hashmap aka 'array'.
 43532   */
 43533  case JX9_OP_CVT_ARRAY:
 43534  #ifdef UNTRUST
 43535  	if( pTos < pStack ){
 43536  		goto Abort;
 43537  	}
 43538  #endif
 43539  	/* Force a hashmap cast */
 43540  	rc = jx9MemObjToHashmap(pTos);
 43541  	if( rc != SXRET_OK ){
 43542  		/* Not so fatal, emit a simple warning */
 43543  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING, 
 43544  			"JX9 engine is running out of memory while performing an array cast");
 43545  	}
 43546  	break;
 43547  /*
 43548   * LOADC P1 P2 *
 43549   *
 43550   * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool.
 43551   * If P1 is set, then this constant is candidate for expansion via user installable callbacks.
 43552   */
 43553  case JX9_OP_LOADC: {
 43554  	jx9_value *pObj;
 43555  	/* Reserve a room */
 43556  	pTos++;
 43557  	if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){
 43558  		if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){
 43559  			SyHashEntry *pEntry;
 43560  			/* Candidate for expansion via user defined callbacks */
 43561  			pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
 43562  			if( pEntry ){
 43563  				jx9_constant *pCons = (jx9_constant *)pEntry->pUserData;
 43564  				/* Set a NULL default value */
 43565  				MemObjSetType(pTos, MEMOBJ_NULL);
 43566  				SyBlobReset(&pTos->sBlob);
 43567  				/* Invoke the callback and deal with the expanded value */
 43568  				pCons->xExpand(pTos, pCons->pUserData);
 43569  				/* Mark as constant */
 43570  				pTos->nIdx = SXU32_HIGH;
 43571  				break;
 43572  			}
 43573  		}
 43574  		jx9MemObjLoad(pObj, pTos);
 43575  	}else{
 43576  		/* Set a NULL value */
 43577  		MemObjSetType(pTos, MEMOBJ_NULL);
 43578  	}
 43579  	/* Mark as constant */
 43580  	pTos->nIdx = SXU32_HIGH;
 43581  	break;
 43582  				  }
 43583  /*
 43584   * LOAD: P1 * P3
 43585   *
 43586   * Load a variable where it's name is taken from the top of the stack or
 43587   * from the P3 operand.
 43588   * If P1 is set, then perform a lookup only.In other words do not create
 43589   * the variable if non existent and push the NULL constant instead.
 43590   */
 43591  case JX9_OP_LOAD:{
 43592  	jx9_value *pObj;
 43593  	SyString sName;
 43594  	if( pInstr->p3 == 0 ){
 43595  		/* Take the variable name from the top of the stack */
 43596  #ifdef UNTRUST
 43597  		if( pTos < pStack ){
 43598  			goto Abort;
 43599  		}
 43600  #endif
 43601  		/* Force a string cast */
 43602  		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 43603  			jx9MemObjToString(pTos);
 43604  		}
 43605  		SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 43606  	}else{
 43607  		SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
 43608  		/* Reserve a room for the target object */
 43609  		pTos++;
 43610  	}
 43611  	/* Extract the requested memory object */
 43612  	pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1);
 43613  	if( pObj == 0 ){
 43614  		if( pInstr->iP1 ){
 43615  			/* Variable not found, load NULL */
 43616  			if( !pInstr->p3 ){
 43617  				jx9MemObjRelease(pTos);
 43618  			}else{
 43619  				MemObjSetType(pTos, MEMOBJ_NULL);
 43620  			}
 43621  			pTos->nIdx = SXU32_HIGH; /* Mark as constant */
 43622  			break;
 43623  		}else{
 43624  			/* Fatal error */
 43625  			VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
 43626  			goto Abort;
 43627  		}
 43628  	}
 43629  	/* Load variable contents */
 43630  	jx9MemObjLoad(pObj, pTos);
 43631  	pTos->nIdx = pObj->nIdx;
 43632  	break;
 43633  				   }
 43634  /*
 43635   * LOAD_MAP P1 * *
 43636   *
 43637   * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack.
 43638   * If the P1 operand is greater than zero then pop P1 elements from the
 43639   * stack and insert them (key => value pair) in the new hashmap.
 43640   */
 43641  case JX9_OP_LOAD_MAP: {
 43642  	jx9_hashmap *pMap;
 43643  	int is_json_object; /* TRUE if we are dealing with a JSON object */
 43644  	int iIncr = 1;
 43645  	/* Allocate a new hashmap instance */
 43646  	pMap = jx9NewHashmap(&(*pVm), 0, 0);
 43647  	if( pMap == 0 ){
 43648  		VmErrorFormat(&(*pVm), JX9_CTX_ERR, 
 43649  			"Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
 43650  		goto Abort;
 43651  	}
 43652  	is_json_object = 0;
 43653  	if( pInstr->iP2 ){
 43654  		/* JSON object, record that */
 43655  		pMap->iFlags |= HASHMAP_JSON_OBJECT;
 43656  		is_json_object = 1;
 43657  		iIncr = 2;
 43658  	}
 43659  	if( pInstr->iP1 > 0 ){
 43660  		jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
 43661  		/* Perform the insertion */
 43662  		while( pEntry <= pTos ){
 43663  			/* Standard insertion */
 43664  			jx9HashmapInsert(pMap, 
 43665  				is_json_object ? pEntry : 0 /* Automatic index assign */,
 43666  				is_json_object ? &pEntry[1] : pEntry
 43667  			);			
 43668  			/* Next pair on the stack */
 43669  			pEntry += iIncr;
 43670  		}
 43671  		/* Pop P1 elements */
 43672  		VmPopOperand(&pTos, pInstr->iP1);
 43673  	}
 43674  	/* Push the hashmap */
 43675  	pTos++;
 43676  	pTos->x.pOther = pMap;
 43677  	MemObjSetType(pTos, MEMOBJ_HASHMAP);
 43678  	break;
 43679  					  }
 43680  /*
 43681   * LOAD_IDX: P1 P2 *
 43682   *
 43683   * Load a hasmap entry where it's index (either numeric or string) is taken
 43684   * from the stack.
 43685   * If the index does not refer to a valid element, then push the NULL constant
 43686   * instead.
 43687   */
 43688  case JX9_OP_LOAD_IDX: {
 43689  	jx9_hashmap_node *pNode = 0; /* cc warning */
 43690  	jx9_hashmap *pMap = 0;
 43691  	jx9_value *pIdx;
 43692  	pIdx = 0;
 43693  	if( pInstr->iP1 == 0 ){
 43694  		if( !pInstr->iP2){
 43695  			/* No available index, load NULL */
 43696  			if( pTos >= pStack ){
 43697  				jx9MemObjRelease(pTos);
 43698  			}else{
 43699  				/* TICKET 1433-020: Empty stack */
 43700  				pTos++;
 43701  				MemObjSetType(pTos, MEMOBJ_NULL);
 43702  				pTos->nIdx = SXU32_HIGH;
 43703  			}
 43704  			/* Emit a notice */
 43705  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE, 
 43706  				"JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL");
 43707  			break;
 43708  		}
 43709  	}else{
 43710  		pIdx = pTos;
 43711  		pTos--;
 43712  	}
 43713  	if( pTos->iFlags & MEMOBJ_STRING ){
 43714  		/* String access */
 43715  		if( pIdx ){
 43716  			sxu32 nOfft;
 43717  			if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){
 43718  				/* Force an int cast */
 43719  				jx9MemObjToInteger(pIdx);
 43720  			}
 43721  			nOfft = (sxu32)pIdx->x.iVal;
 43722  			if( nOfft >= SyBlobLength(&pTos->sBlob) ){
 43723  				/* Invalid offset, load null */
 43724  				jx9MemObjRelease(pTos);
 43725  			}else{
 43726  				const char *zData = (const char *)SyBlobData(&pTos->sBlob);
 43727  				int c = zData[nOfft];
 43728  				jx9MemObjRelease(pTos);
 43729  				MemObjSetType(pTos, MEMOBJ_STRING);
 43730  				SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char));
 43731  			}
 43732  		}else{
 43733  			/* No available index, load NULL */
 43734  			MemObjSetType(pTos, MEMOBJ_NULL);
 43735  		}
 43736  		break;
 43737  	}
 43738  	if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){
 43739  		if( pTos->nIdx != SXU32_HIGH ){
 43740  			jx9_value *pObj;
 43741  			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 43742  				jx9MemObjToHashmap(pObj);
 43743  				jx9MemObjLoad(pObj, pTos);
 43744  			}
 43745  		}
 43746  	}
 43747  	rc = SXERR_NOTFOUND; /* Assume the index is invalid */
 43748  	if( pTos->iFlags & MEMOBJ_HASHMAP ){
 43749  		/* Point to the hashmap */
 43750  		pMap = (jx9_hashmap *)pTos->x.pOther;
 43751  		if( pIdx ){
 43752  			/* Load the desired entry */
 43753  			rc = jx9HashmapLookup(pMap, pIdx, &pNode);
 43754  		}
 43755  		if( rc != SXRET_OK && pInstr->iP2 ){
 43756  			/* Create a new empty entry */
 43757  			rc = jx9HashmapInsert(pMap, pIdx, 0);
 43758  			if( rc == SXRET_OK ){
 43759  				/* Point to the last inserted entry */
 43760  				pNode = pMap->pLast;
 43761  			}
 43762  		}
 43763  	}
 43764  	if( pIdx ){
 43765  		jx9MemObjRelease(pIdx);
 43766  	}
 43767  	if( rc == SXRET_OK ){
 43768  		/* Load entry contents */
 43769  		if( pMap->iRef < 2 ){
 43770  			/* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
 43771  			 * of the entry value, rather than pointing to it.
 43772  			 */
 43773  			pTos->nIdx = SXU32_HIGH;
 43774  			jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
 43775  		}else{
 43776  			pTos->nIdx = pNode->nValIdx;
 43777  			jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
 43778  			jx9HashmapUnref(pMap);
 43779  		}
 43780  	}else{
 43781  		/* No such entry, load NULL */
 43782  		jx9MemObjRelease(pTos);
 43783  		pTos->nIdx = SXU32_HIGH;
 43784  	}
 43785  	break;
 43786  					  }
 43787  /*
 43788   * STORE * P2 P3
 43789   *
 43790   * Perform a store (Assignment) operation.
 43791   */
 43792  case JX9_OP_STORE: {
 43793  	jx9_value *pObj;
 43794  	SyString sName;
 43795  #ifdef UNTRUST
 43796  	if( pTos < pStack ){
 43797  		goto Abort;
 43798  	}
 43799  #endif
 43800  	if( pInstr->iP2 ){
 43801  		sxu32 nIdx;
 43802  		/* Member store operation */
 43803  		nIdx = pTos->nIdx;
 43804  		VmPopOperand(&pTos, 1);
 43805  		if( nIdx == SXU32_HIGH ){
 43806  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, 
 43807  				"Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
 43808  			pTos->nIdx = SXU32_HIGH;
 43809  		}else{
 43810  			/* Point to the desired memory object */
 43811  			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 43812  			if( pObj ){
 43813  				/* Perform the store operation */
 43814  				jx9MemObjStore(pTos, pObj);
 43815  			}
 43816  		}
 43817  		break;
 43818  	}else if( pInstr->p3 == 0 ){
 43819  		/* Take the variable name from the next on the stack */
 43820  		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 43821  			/* Force a string cast */
 43822  			jx9MemObjToString(pTos);
 43823  		}
 43824  		SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 43825  		pTos--;
 43826  #ifdef UNTRUST
 43827  		if( pTos < pStack  ){
 43828  			goto Abort;
 43829  		}
 43830  #endif
 43831  	}else{
 43832  		SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
 43833  	}
 43834  	/* Extract the desired variable and if not available dynamically create it */
 43835  	pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE);
 43836  	if( pObj == 0 ){
 43837  		VmErrorFormat(&(*pVm), JX9_CTX_ERR, 
 43838  			"Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
 43839  		goto Abort;
 43840  	}
 43841  	if( !pInstr->p3 ){
 43842  		jx9MemObjRelease(&pTos[1]);
 43843  	}
 43844  	/* Perform the store operation */
 43845  	jx9MemObjStore(pTos, pObj);
 43846  	break;
 43847  				   }
 43848  /*
 43849   * STORE_IDX:   P1 * P3
 43850   *
 43851   * Perfrom a store operation an a hashmap entry.
 43852   */
 43853  case JX9_OP_STORE_IDX: {
 43854  	jx9_hashmap *pMap = 0; /* cc  warning */
 43855  	jx9_value *pKey;
 43856  	sxu32 nIdx;
 43857  	if( pInstr->iP1 ){
 43858  		/* Key is next on stack */
 43859  		pKey = pTos;
 43860  		pTos--;
 43861  	}else{
 43862  		pKey = 0;
 43863  	}
 43864  	nIdx = pTos->nIdx;
 43865  	if( pTos->iFlags & MEMOBJ_HASHMAP ){
 43866  		/* Hashmap already loaded */
 43867  		pMap = (jx9_hashmap *)pTos->x.pOther;
 43868  		if( pMap->iRef < 2 ){
 43869  			/* TICKET 1433-48: Prevent garbage collection */
 43870  			pMap->iRef = 2;
 43871  		}
 43872  	}else{
 43873  		jx9_value *pObj;
 43874  		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
 43875  		if( pObj == 0 ){
 43876  			if( pKey ){
 43877  			  jx9MemObjRelease(pKey);
 43878  			}
 43879  			VmPopOperand(&pTos, 1);
 43880  			break;
 43881  		}
 43882  		/* Phase#1: Load the array */
 43883  		if( (pObj->iFlags & MEMOBJ_STRING)  ){
 43884  			VmPopOperand(&pTos, 1);
 43885  			if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
 43886  				/* Force a string cast */
 43887  				jx9MemObjToString(pTos);
 43888  			}
 43889  			if( pKey == 0 ){
 43890  				/* Append string */
 43891  				if( SyBlobLength(&pTos->sBlob) > 0 ){
 43892  					SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 43893  				}
 43894  			}else{
 43895  				sxu32 nOfft;
 43896  				if((pKey->iFlags & MEMOBJ_INT)){
 43897  					/* Force an int cast */
 43898  					jx9MemObjToInteger(pKey);
 43899  				}
 43900  				nOfft = (sxu32)pKey->x.iVal;
 43901  				if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){
 43902  					const char *zBlob = (const char *)SyBlobData(&pTos->sBlob);
 43903  					char *zData = (char *)SyBlobData(&pObj->sBlob);
 43904  					zData[nOfft] = zBlob[0];
 43905  				}else{
 43906  					if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){
 43907  						/* Perform an append operation */
 43908  						SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char));
 43909  					}
 43910  				}
 43911  			}
 43912  			if( pKey ){
 43913  			  jx9MemObjRelease(pKey);
 43914  			}
 43915  			break;
 43916  		}else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
 43917  			/* Force a hashmap cast  */
 43918  			rc = jx9MemObjToHashmap(pObj);
 43919  			if( rc != SXRET_OK ){
 43920  				VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
 43921  				goto Abort;
 43922  			}
 43923  		}
 43924  		pMap = (jx9_hashmap *)pObj->x.pOther;
 43925  	}
 43926  	VmPopOperand(&pTos, 1);
 43927  	/* Phase#2: Perform the insertion */
 43928  	jx9HashmapInsert(pMap, pKey, pTos);	
 43929  	if( pKey ){
 43930  		jx9MemObjRelease(pKey);
 43931  	}
 43932  	break;
 43933  					   }
 43934  /*
 43935   * INCR: P1 * *
 43936   *
 43937   * Force a numeric cast and increment the top of the stack by 1.
 43938   * If the P1 operand is set then perform a duplication of the top of
 43939   * the stack and increment after that.
 43940   */
 43941  case JX9_OP_INCR:
 43942  #ifdef UNTRUST
 43943  	if( pTos < pStack ){
 43944  		goto Abort;
 43945  	}
 43946  #endif
 43947  	if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
 43948  		if( pTos->nIdx != SXU32_HIGH ){
 43949  			jx9_value *pObj;
 43950  			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 43951  				/* Force a numeric cast */
 43952  				jx9MemObjToNumeric(pObj);
 43953  				if( pObj->iFlags & MEMOBJ_REAL ){
 43954  					pObj->x.rVal++;
 43955  					/* Try to get an integer representation */
 43956  					jx9MemObjTryInteger(pTos);
 43957  				}else{
 43958  					pObj->x.iVal++;
 43959  					MemObjSetType(pTos, MEMOBJ_INT);
 43960  				}
 43961  				if( pInstr->iP1 ){
 43962  					/* Pre-icrement */
 43963  					jx9MemObjStore(pObj, pTos);
 43964  				}
 43965  			}
 43966  		}else{
 43967  			if( pInstr->iP1 ){
 43968  				/* Force a numeric cast */
 43969  				jx9MemObjToNumeric(pTos);
 43970  				/* Pre-increment */
 43971  				if( pTos->iFlags & MEMOBJ_REAL ){
 43972  					pTos->x.rVal++;
 43973  					/* Try to get an integer representation */
 43974  					jx9MemObjTryInteger(pTos);
 43975  				}else{
 43976  					pTos->x.iVal++;
 43977  					MemObjSetType(pTos, MEMOBJ_INT);
 43978  				}
 43979  			}
 43980  		}
 43981  	}
 43982  	break;
 43983  /*
 43984   * DECR: P1 * *
 43985   *
 43986   * Force a numeric cast and decrement the top of the stack by 1.
 43987   * If the P1 operand is set then perform a duplication of the top of the stack 
 43988   * and decrement after that.
 43989   */
 43990  case JX9_OP_DECR:
 43991  #ifdef UNTRUST
 43992  	if( pTos < pStack ){
 43993  		goto Abort;
 43994  	}
 43995  #endif
 43996  	if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
 43997  		/* Force a numeric cast */
 43998  		jx9MemObjToNumeric(pTos);
 43999  		if( pTos->nIdx != SXU32_HIGH ){
 44000  			jx9_value *pObj;
 44001  			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44002  				/* Force a numeric cast */
 44003  				jx9MemObjToNumeric(pObj);
 44004  				if( pObj->iFlags & MEMOBJ_REAL ){
 44005  					pObj->x.rVal--;
 44006  					/* Try to get an integer representation */
 44007  					jx9MemObjTryInteger(pTos);
 44008  				}else{
 44009  					pObj->x.iVal--;
 44010  					MemObjSetType(pTos, MEMOBJ_INT);
 44011  				}
 44012  				if( pInstr->iP1 ){
 44013  					/* Pre-icrement */
 44014  					jx9MemObjStore(pObj, pTos);
 44015  				}
 44016  			}
 44017  		}else{
 44018  			if( pInstr->iP1 ){
 44019  				/* Pre-increment */
 44020  				if( pTos->iFlags & MEMOBJ_REAL ){
 44021  					pTos->x.rVal--;
 44022  					/* Try to get an integer representation */
 44023  					jx9MemObjTryInteger(pTos);
 44024  				}else{
 44025  					pTos->x.iVal--;
 44026  					MemObjSetType(pTos, MEMOBJ_INT);
 44027  				}
 44028  			}
 44029  		}
 44030  	}
 44031  	break;
 44032  /*
 44033   * UMINUS: * * *
 44034   *
 44035   * Perform a unary minus operation.
 44036   */
 44037  case JX9_OP_UMINUS:
 44038  #ifdef UNTRUST
 44039  	if( pTos < pStack ){
 44040  		goto Abort;
 44041  	}
 44042  #endif
 44043  	/* Force a numeric (integer, real or both) cast */
 44044  	jx9MemObjToNumeric(pTos);
 44045  	if( pTos->iFlags & MEMOBJ_REAL ){
 44046  		pTos->x.rVal = -pTos->x.rVal;
 44047  	}
 44048  	if( pTos->iFlags & MEMOBJ_INT ){
 44049  		pTos->x.iVal = -pTos->x.iVal;
 44050  	}
 44051  	break;				   
 44052  /*
 44053   * UPLUS: * * *
 44054   *
 44055   * Perform a unary plus operation.
 44056   */
 44057  case JX9_OP_UPLUS:
 44058  #ifdef UNTRUST
 44059  	if( pTos < pStack ){
 44060  		goto Abort;
 44061  	}
 44062  #endif
 44063  	/* Force a numeric (integer, real or both) cast */
 44064  	jx9MemObjToNumeric(pTos);
 44065  	if( pTos->iFlags & MEMOBJ_REAL ){
 44066  		pTos->x.rVal = +pTos->x.rVal;
 44067  	}
 44068  	if( pTos->iFlags & MEMOBJ_INT ){
 44069  		pTos->x.iVal = +pTos->x.iVal;
 44070  	}
 44071  	break;
 44072  /*
 44073   * OP_LNOT: * * *
 44074   *
 44075   * Interpret the top of the stack as a boolean value.  Replace it
 44076   * with its complement.
 44077   */
 44078  case JX9_OP_LNOT:
 44079  #ifdef UNTRUST
 44080  	if( pTos < pStack ){
 44081  		goto Abort;
 44082  	}
 44083  #endif
 44084  	/* Force a boolean cast */
 44085  	if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 44086  		jx9MemObjToBool(pTos);
 44087  	}
 44088  	pTos->x.iVal = !pTos->x.iVal;
 44089  	break;
 44090  /*
 44091   * OP_BITNOT: * * *
 44092   *
 44093   * Interpret the top of the stack as an value.Replace it
 44094   * with its ones-complement.
 44095   */
 44096  case JX9_OP_BITNOT:
 44097  #ifdef UNTRUST
 44098  	if( pTos < pStack ){
 44099  		goto Abort;
 44100  	}
 44101  #endif
 44102  	/* Force an integer cast */
 44103  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44104  		jx9MemObjToInteger(pTos);
 44105  	}
 44106  	pTos->x.iVal = ~pTos->x.iVal;
 44107  	break;
 44108  /* OP_MUL * * *
 44109   * OP_MUL_STORE * * *
 44110   *
 44111   * Pop the top two elements from the stack, multiply them together, 
 44112   * and push the result back onto the stack.
 44113   */
 44114  case JX9_OP_MUL:
 44115  case JX9_OP_MUL_STORE: {
 44116  	jx9_value *pNos = &pTos[-1];
 44117  	/* Force the operand to be numeric */
 44118  #ifdef UNTRUST
 44119  	if( pNos < pStack ){
 44120  		goto Abort;
 44121  	}
 44122  #endif
 44123  	jx9MemObjToNumeric(pTos);
 44124  	jx9MemObjToNumeric(pNos);
 44125  	/* Perform the requested operation */
 44126  	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
 44127  		/* Floating point arithemic */
 44128  		jx9_real a, b, r;
 44129  		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
 44130  			jx9MemObjToReal(pTos);
 44131  		}
 44132  		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
 44133  			jx9MemObjToReal(pNos);
 44134  		}
 44135  		a = pNos->x.rVal;
 44136  		b = pTos->x.rVal;
 44137  		r = a * b;
 44138  		/* Push the result */
 44139  		pNos->x.rVal = r;
 44140  		MemObjSetType(pNos, MEMOBJ_REAL);
 44141  		/* Try to get an integer representation */
 44142  		jx9MemObjTryInteger(pNos);
 44143  	}else{
 44144  		/* Integer arithmetic */
 44145  		sxi64 a, b, r;
 44146  		a = pNos->x.iVal;
 44147  		b = pTos->x.iVal;
 44148  		r = a * b;
 44149  		/* Push the result */
 44150  		pNos->x.iVal = r;
 44151  		MemObjSetType(pNos, MEMOBJ_INT);
 44152  	}
 44153  	if( pInstr->iOp == JX9_OP_MUL_STORE ){
 44154  		jx9_value *pObj;
 44155  		if( pTos->nIdx == SXU32_HIGH ){
 44156  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44157  		}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44158  			jx9MemObjStore(pNos, pObj);
 44159  		}
 44160  	}
 44161  	VmPopOperand(&pTos, 1);
 44162  	break;
 44163  				 }
 44164  /* OP_ADD * * *
 44165   *
 44166   * Pop the top two elements from the stack, add them together, 
 44167   * and push the result back onto the stack.
 44168   */
 44169  case JX9_OP_ADD:{
 44170  	jx9_value *pNos = &pTos[-1];
 44171  #ifdef UNTRUST
 44172  	if( pNos < pStack ){
 44173  		goto Abort;
 44174  	}
 44175  #endif
 44176  	/* Perform the addition */
 44177  	jx9MemObjAdd(pNos, pTos, FALSE);
 44178  	VmPopOperand(&pTos, 1);
 44179  	break;
 44180  				}
 44181  /*
 44182   * OP_ADD_STORE * * *
 44183   *
 44184   * Pop the top two elements from the stack, add them together, 
 44185   * and push the result back onto the stack.
 44186   */
 44187  case JX9_OP_ADD_STORE:{
 44188  	jx9_value *pNos = &pTos[-1];
 44189  	jx9_value *pObj;
 44190  	sxu32 nIdx;
 44191  #ifdef UNTRUST
 44192  	if( pNos < pStack ){
 44193  		goto Abort;
 44194  	}
 44195  #endif
 44196  	/* Perform the addition */
 44197  	nIdx = pTos->nIdx;
 44198  	jx9MemObjAdd(pTos, pNos, TRUE);
 44199  	/* Peform the store operation */
 44200  	if( nIdx == SXU32_HIGH ){
 44201  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44202  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
 44203  		jx9MemObjStore(pTos, pObj);
 44204  	}
 44205  	/* Ticket 1433-35: Perform a stack dup */
 44206  	jx9MemObjStore(pTos, pNos);
 44207  	VmPopOperand(&pTos, 1);
 44208  	break;
 44209  				}
 44210  /* OP_SUB * * *
 44211   *
 44212   * Pop the top two elements from the stack, subtract the
 44213   * first (what was next on the stack) from the second (the
 44214   * top of the stack) and push the result back onto the stack.
 44215   */
 44216  case JX9_OP_SUB: {
 44217  	jx9_value *pNos = &pTos[-1];
 44218  #ifdef UNTRUST
 44219  	if( pNos < pStack ){
 44220  		goto Abort;
 44221  	}
 44222  #endif
 44223  	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
 44224  		/* Floating point arithemic */
 44225  		jx9_real a, b, r;
 44226  		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
 44227  			jx9MemObjToReal(pTos);
 44228  		}
 44229  		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
 44230  			jx9MemObjToReal(pNos);
 44231  		}
 44232  		a = pNos->x.rVal;
 44233  		b = pTos->x.rVal;
 44234  		r = a - b; 
 44235  		/* Push the result */
 44236  		pNos->x.rVal = r;
 44237  		MemObjSetType(pNos, MEMOBJ_REAL);
 44238  		/* Try to get an integer representation */
 44239  		jx9MemObjTryInteger(pNos);
 44240  	}else{
 44241  		/* Integer arithmetic */
 44242  		sxi64 a, b, r;
 44243  		a = pNos->x.iVal;
 44244  		b = pTos->x.iVal;
 44245  		r = a - b;
 44246  		/* Push the result */
 44247  		pNos->x.iVal = r;
 44248  		MemObjSetType(pNos, MEMOBJ_INT);
 44249  	}
 44250  	VmPopOperand(&pTos, 1);
 44251  	break;
 44252  				 }
 44253  /* OP_SUB_STORE * * *
 44254   *
 44255   * Pop the top two elements from the stack, subtract the
 44256   * first (what was next on the stack) from the second (the
 44257   * top of the stack) and push the result back onto the stack.
 44258   */
 44259  case JX9_OP_SUB_STORE: {
 44260  	jx9_value *pNos = &pTos[-1];
 44261  	jx9_value *pObj;
 44262  #ifdef UNTRUST
 44263  	if( pNos < pStack ){
 44264  		goto Abort;
 44265  	}
 44266  #endif
 44267  	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
 44268  		/* Floating point arithemic */
 44269  		jx9_real a, b, r;
 44270  		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
 44271  			jx9MemObjToReal(pTos);
 44272  		}
 44273  		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
 44274  			jx9MemObjToReal(pNos);
 44275  		}
 44276  		a = pTos->x.rVal;
 44277  		b = pNos->x.rVal;
 44278  		r = a - b; 
 44279  		/* Push the result */
 44280  		pNos->x.rVal = r;
 44281  		MemObjSetType(pNos, MEMOBJ_REAL);
 44282  		/* Try to get an integer representation */
 44283  		jx9MemObjTryInteger(pNos);
 44284  	}else{
 44285  		/* Integer arithmetic */
 44286  		sxi64 a, b, r;
 44287  		a = pTos->x.iVal;
 44288  		b = pNos->x.iVal;
 44289  		r = a - b;
 44290  		/* Push the result */
 44291  		pNos->x.iVal = r;
 44292  		MemObjSetType(pNos, MEMOBJ_INT);
 44293  	}
 44294  	if( pTos->nIdx == SXU32_HIGH ){
 44295  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44296  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44297  		jx9MemObjStore(pNos, pObj);
 44298  	}
 44299  	VmPopOperand(&pTos, 1);
 44300  	break;
 44301  				 }
 44302  
 44303  /*
 44304   * OP_MOD * * *
 44305   *
 44306   * Pop the top two elements from the stack, divide the
 44307   * first (what was next on the stack) from the second (the
 44308   * top of the stack) and push the remainder after division 
 44309   * onto the stack.
 44310   * Note: Only integer arithemtic is allowed.
 44311   */
 44312  case JX9_OP_MOD:{
 44313  	jx9_value *pNos = &pTos[-1];
 44314  	sxi64 a, b, r;
 44315  #ifdef UNTRUST
 44316  	if( pNos < pStack ){
 44317  		goto Abort;
 44318  	}
 44319  #endif
 44320  	/* Force the operands to be integer */
 44321  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44322  		jx9MemObjToInteger(pTos);
 44323  	}
 44324  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44325  		jx9MemObjToInteger(pNos);
 44326  	}
 44327  	/* Perform the requested operation */
 44328  	a = pNos->x.iVal;
 44329  	b = pTos->x.iVal;
 44330  	if( b == 0 ){
 44331  		r = 0;
 44332  		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
 44333  		/* goto Abort; */
 44334  	}else{
 44335  		r = a%b;
 44336  	}
 44337  	/* Push the result */
 44338  	pNos->x.iVal = r;
 44339  	MemObjSetType(pNos, MEMOBJ_INT);
 44340  	VmPopOperand(&pTos, 1);
 44341  	break;
 44342  				}
 44343  /*
 44344   * OP_MOD_STORE * * *
 44345   *
 44346   * Pop the top two elements from the stack, divide the
 44347   * first (what was next on the stack) from the second (the
 44348   * top of the stack) and push the remainder after division 
 44349   * onto the stack.
 44350   * Note: Only integer arithemtic is allowed.
 44351   */
 44352  case JX9_OP_MOD_STORE: {
 44353  	jx9_value *pNos = &pTos[-1];
 44354  	jx9_value *pObj;
 44355  	sxi64 a, b, r;
 44356  #ifdef UNTRUST
 44357  	if( pNos < pStack ){
 44358  		goto Abort;
 44359  	}
 44360  #endif
 44361  	/* Force the operands to be integer */
 44362  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44363  		jx9MemObjToInteger(pTos);
 44364  	}
 44365  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44366  		jx9MemObjToInteger(pNos);
 44367  	}
 44368  	/* Perform the requested operation */
 44369  	a = pTos->x.iVal;
 44370  	b = pNos->x.iVal;
 44371  	if( b == 0 ){
 44372  		r = 0;
 44373  		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
 44374  		/* goto Abort; */
 44375  	}else{
 44376  		r = a%b;
 44377  	}
 44378  	/* Push the result */
 44379  	pNos->x.iVal = r;
 44380  	MemObjSetType(pNos, MEMOBJ_INT);
 44381  	if( pTos->nIdx == SXU32_HIGH ){
 44382  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44383  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44384  		jx9MemObjStore(pNos, pObj);
 44385  	}
 44386  	VmPopOperand(&pTos, 1);
 44387  	break;
 44388  				}
 44389  /*
 44390   * OP_DIV * * *
 44391   * 
 44392   * Pop the top two elements from the stack, divide the
 44393   * first (what was next on the stack) from the second (the
 44394   * top of the stack) and push the result onto the stack.
 44395   * Note: Only floating point arithemtic is allowed.
 44396   */
 44397  case JX9_OP_DIV:{
 44398  	jx9_value *pNos = &pTos[-1];
 44399  	jx9_real a, b, r;
 44400  #ifdef UNTRUST
 44401  	if( pNos < pStack ){
 44402  		goto Abort;
 44403  	}
 44404  #endif
 44405  	/* Force the operands to be real */
 44406  	if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
 44407  		jx9MemObjToReal(pTos);
 44408  	}
 44409  	if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
 44410  		jx9MemObjToReal(pNos);
 44411  	}
 44412  	/* Perform the requested operation */
 44413  	a = pNos->x.rVal;
 44414  	b = pTos->x.rVal;
 44415  	if( b == 0 ){
 44416  		/* Division by zero */
 44417  		r = 0;
 44418  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
 44419  		/* goto Abort; */
 44420  	}else{
 44421  		r = a/b;
 44422  		/* Push the result */
 44423  		pNos->x.rVal = r;
 44424  		MemObjSetType(pNos, MEMOBJ_REAL);
 44425  		/* Try to get an integer representation */
 44426  		jx9MemObjTryInteger(pNos);
 44427  	}
 44428  	VmPopOperand(&pTos, 1);
 44429  	break;
 44430  				}
 44431  /*
 44432   * OP_DIV_STORE * * *
 44433   * 
 44434   * Pop the top two elements from the stack, divide the
 44435   * first (what was next on the stack) from the second (the
 44436   * top of the stack) and push the result onto the stack.
 44437   * Note: Only floating point arithemtic is allowed.
 44438   */
 44439  case JX9_OP_DIV_STORE:{
 44440  	jx9_value *pNos = &pTos[-1];
 44441  	jx9_value *pObj;
 44442  	jx9_real a, b, r;
 44443  #ifdef UNTRUST
 44444  	if( pNos < pStack ){
 44445  		goto Abort;
 44446  	}
 44447  #endif
 44448  	/* Force the operands to be real */
 44449  	if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
 44450  		jx9MemObjToReal(pTos);
 44451  	}
 44452  	if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
 44453  		jx9MemObjToReal(pNos);
 44454  	}
 44455  	/* Perform the requested operation */
 44456  	a = pTos->x.rVal;
 44457  	b = pNos->x.rVal;
 44458  	if( b == 0 ){
 44459  		/* Division by zero */
 44460  		r = 0;
 44461  		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
 44462  		/* goto Abort; */
 44463  	}else{
 44464  		r = a/b;
 44465  		/* Push the result */
 44466  		pNos->x.rVal = r;
 44467  		MemObjSetType(pNos, MEMOBJ_REAL);
 44468  		/* Try to get an integer representation */
 44469  		jx9MemObjTryInteger(pNos);
 44470  	}
 44471  	if( pTos->nIdx == SXU32_HIGH ){
 44472  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44473  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44474  		jx9MemObjStore(pNos, pObj);
 44475  	}
 44476  	VmPopOperand(&pTos, 1);
 44477  	break;
 44478  				}
 44479  /* OP_BAND * * *
 44480   *
 44481   * Pop the top two elements from the stack.  Convert both elements
 44482   * to integers.  Push back onto the stack the bit-wise AND of the
 44483   * two elements.
 44484  */
 44485  /* OP_BOR * * *
 44486   *
 44487   * Pop the top two elements from the stack.  Convert both elements
 44488   * to integers.  Push back onto the stack the bit-wise OR of the
 44489   * two elements.
 44490   */
 44491  /* OP_BXOR * * *
 44492   *
 44493   * Pop the top two elements from the stack.  Convert both elements
 44494   * to integers.  Push back onto the stack the bit-wise XOR of the
 44495   * two elements.
 44496   */
 44497  case JX9_OP_BAND:
 44498  case JX9_OP_BOR:
 44499  case JX9_OP_BXOR:{
 44500  	jx9_value *pNos = &pTos[-1];
 44501  	sxi64 a, b, r;
 44502  #ifdef UNTRUST
 44503  	if( pNos < pStack ){
 44504  		goto Abort;
 44505  	}
 44506  #endif
 44507  	/* Force the operands to be integer */
 44508  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44509  		jx9MemObjToInteger(pTos);
 44510  	}
 44511  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44512  		jx9MemObjToInteger(pNos);
 44513  	}
 44514  	/* Perform the requested operation */
 44515  	a = pNos->x.iVal;
 44516  	b = pTos->x.iVal;
 44517  	switch(pInstr->iOp){
 44518  	case JX9_OP_BOR_STORE:
 44519  	case JX9_OP_BOR:  r = a|b; break;
 44520  	case JX9_OP_BXOR_STORE:
 44521  	case JX9_OP_BXOR: r = a^b; break;
 44522  	case JX9_OP_BAND_STORE:
 44523  	case JX9_OP_BAND:
 44524  	default:          r = a&b; break;
 44525  	}
 44526  	/* Push the result */
 44527  	pNos->x.iVal = r;
 44528  	MemObjSetType(pNos, MEMOBJ_INT);
 44529  	VmPopOperand(&pTos, 1);
 44530  	break;
 44531  				 }
 44532  /* OP_BAND_STORE * * * 
 44533   *
 44534   * Pop the top two elements from the stack.  Convert both elements
 44535   * to integers.  Push back onto the stack the bit-wise AND of the
 44536   * two elements.
 44537  */
 44538  /* OP_BOR_STORE * * *
 44539   *
 44540   * Pop the top two elements from the stack.  Convert both elements
 44541   * to integers.  Push back onto the stack the bit-wise OR of the
 44542   * two elements.
 44543   */
 44544  /* OP_BXOR_STORE * * *
 44545   *
 44546   * Pop the top two elements from the stack.  Convert both elements
 44547   * to integers.  Push back onto the stack the bit-wise XOR of the
 44548   * two elements.
 44549   */
 44550  case JX9_OP_BAND_STORE:
 44551  case JX9_OP_BOR_STORE:
 44552  case JX9_OP_BXOR_STORE:{
 44553  	jx9_value *pNos = &pTos[-1];
 44554  	jx9_value *pObj;
 44555  	sxi64 a, b, r;
 44556  #ifdef UNTRUST
 44557  	if( pNos < pStack ){
 44558  		goto Abort;
 44559  	}
 44560  #endif
 44561  	/* Force the operands to be integer */
 44562  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44563  		jx9MemObjToInteger(pTos);
 44564  	}
 44565  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44566  		jx9MemObjToInteger(pNos);
 44567  	}
 44568  	/* Perform the requested operation */
 44569  	a = pTos->x.iVal;
 44570  	b = pNos->x.iVal;
 44571  	switch(pInstr->iOp){
 44572  	case JX9_OP_BOR_STORE:
 44573  	case JX9_OP_BOR:  r = a|b; break;
 44574  	case JX9_OP_BXOR_STORE:
 44575  	case JX9_OP_BXOR: r = a^b; break;
 44576  	case JX9_OP_BAND_STORE:
 44577  	case JX9_OP_BAND:
 44578  	default:          r = a&b; break;
 44579  	}
 44580  	/* Push the result */
 44581  	pNos->x.iVal = r;
 44582  	MemObjSetType(pNos, MEMOBJ_INT);
 44583  	if( pTos->nIdx == SXU32_HIGH ){
 44584  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44585  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44586  		jx9MemObjStore(pNos, pObj);
 44587  	}
 44588  	VmPopOperand(&pTos, 1);
 44589  	break;
 44590  				 }
 44591  /* OP_SHL * * *
 44592   *
 44593   * Pop the top two elements from the stack.  Convert both elements
 44594   * to integers.  Push back onto the stack the second element shifted
 44595   * left by N bits where N is the top element on the stack.
 44596   * Note: Only integer arithmetic is allowed.
 44597   */
 44598  /* OP_SHR * * *
 44599   *
 44600   * Pop the top two elements from the stack.  Convert both elements
 44601   * to integers.  Push back onto the stack the second element shifted
 44602   * right by N bits where N is the top element on the stack.
 44603   * Note: Only integer arithmetic is allowed.
 44604   */
 44605  case JX9_OP_SHL:
 44606  case JX9_OP_SHR: {
 44607  	jx9_value *pNos = &pTos[-1];
 44608  	sxi64 a, r;
 44609  	sxi32 b;
 44610  #ifdef UNTRUST
 44611  	if( pNos < pStack ){
 44612  		goto Abort;
 44613  	}
 44614  #endif
 44615  	/* Force the operands to be integer */
 44616  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44617  		jx9MemObjToInteger(pTos);
 44618  	}
 44619  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44620  		jx9MemObjToInteger(pNos);
 44621  	}
 44622  	/* Perform the requested operation */
 44623  	a = pNos->x.iVal;
 44624  	b = (sxi32)pTos->x.iVal;
 44625  	if( pInstr->iOp == JX9_OP_SHL ){
 44626  		r = a << b;
 44627  	}else{
 44628  		r = a >> b;
 44629  	}
 44630  	/* Push the result */
 44631  	pNos->x.iVal = r;
 44632  	MemObjSetType(pNos, MEMOBJ_INT);
 44633  	VmPopOperand(&pTos, 1);
 44634  	break;
 44635  				 }
 44636  /*  OP_SHL_STORE * * *
 44637   *
 44638   * Pop the top two elements from the stack.  Convert both elements
 44639   * to integers.  Push back onto the stack the second element shifted
 44640   * left by N bits where N is the top element on the stack.
 44641   * Note: Only integer arithmetic is allowed.
 44642   */
 44643  /* OP_SHR_STORE * * *
 44644   *
 44645   * Pop the top two elements from the stack.  Convert both elements
 44646   * to integers.  Push back onto the stack the second element shifted
 44647   * right by N bits where N is the top element on the stack.
 44648   * Note: Only integer arithmetic is allowed.
 44649   */
 44650  case JX9_OP_SHL_STORE:
 44651  case JX9_OP_SHR_STORE: {
 44652  	jx9_value *pNos = &pTos[-1];
 44653  	jx9_value *pObj;
 44654  	sxi64 a, r;
 44655  	sxi32 b;
 44656  #ifdef UNTRUST
 44657  	if( pNos < pStack ){
 44658  		goto Abort;
 44659  	}
 44660  #endif
 44661  	/* Force the operands to be integer */
 44662  	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
 44663  		jx9MemObjToInteger(pTos);
 44664  	}
 44665  	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
 44666  		jx9MemObjToInteger(pNos);
 44667  	}
 44668  	/* Perform the requested operation */
 44669  	a = pTos->x.iVal;
 44670  	b = (sxi32)pNos->x.iVal;
 44671  	if( pInstr->iOp == JX9_OP_SHL_STORE ){
 44672  		r = a << b;
 44673  	}else{
 44674  		r = a >> b;
 44675  	}
 44676  	/* Push the result */
 44677  	pNos->x.iVal = r;
 44678  	MemObjSetType(pNos, MEMOBJ_INT);
 44679  	if( pTos->nIdx == SXU32_HIGH ){
 44680  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44681  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44682  		jx9MemObjStore(pNos, pObj);
 44683  	}
 44684  	VmPopOperand(&pTos, 1);
 44685  	break;
 44686  				 }
 44687  /* CAT:  P1 * *
 44688   *
 44689   * Pop P1 elements from the stack. Concatenate them togeher and push the result
 44690   * back.
 44691   */
 44692  case JX9_OP_CAT:{
 44693  	jx9_value *pNos, *pCur;
 44694  	if( pInstr->iP1 < 1 ){
 44695  		pNos = &pTos[-1];
 44696  	}else{
 44697  		pNos = &pTos[-pInstr->iP1+1];
 44698  	}
 44699  #ifdef UNTRUST
 44700  	if( pNos < pStack ){
 44701  		goto Abort;
 44702  	}
 44703  #endif
 44704  	/* Force a string cast */
 44705  	if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){
 44706  		jx9MemObjToString(pNos);
 44707  	}
 44708  	pCur = &pNos[1];
 44709  	while( pCur <= pTos ){
 44710  		if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
 44711  			jx9MemObjToString(pCur);
 44712  		}
 44713  		/* Perform the concatenation */
 44714  		if( SyBlobLength(&pCur->sBlob) > 0 ){
 44715  			jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob));
 44716  		}
 44717  		SyBlobRelease(&pCur->sBlob);
 44718  		pCur++;
 44719  	}
 44720  	pTos = pNos;
 44721  	break;
 44722  				}
 44723  /*  CAT_STORE: * * *
 44724   *
 44725   * Pop two elements from the stack. Concatenate them togeher and push the result
 44726   * back.
 44727   */
 44728  case JX9_OP_CAT_STORE:{
 44729  	jx9_value *pNos = &pTos[-1];
 44730  	jx9_value *pObj;
 44731  #ifdef UNTRUST
 44732  	if( pNos < pStack ){
 44733  		goto Abort;
 44734  	}
 44735  #endif
 44736  	if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
 44737  		/* Force a string cast */
 44738  		jx9MemObjToString(pTos);
 44739  	}
 44740  	if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
 44741  		/* Force a string cast */
 44742  		jx9MemObjToString(pNos);
 44743  	}
 44744  	/* Perform the concatenation (Reverse order) */
 44745  	if( SyBlobLength(&pNos->sBlob) > 0 ){
 44746  		jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
 44747  	}
 44748  	/* Perform the store operation */
 44749  	if( pTos->nIdx == SXU32_HIGH ){
 44750  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
 44751  	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
 44752  		jx9MemObjStore(pTos, pObj);
 44753  	}
 44754  	jx9MemObjStore(pTos, pNos);
 44755  	VmPopOperand(&pTos, 1);
 44756  	break;
 44757  				}
 44758  /* OP_AND: * * *
 44759   *
 44760   * Pop two values off the stack.  Take the logical AND of the
 44761   * two values and push the resulting boolean value back onto the
 44762   * stack. 
 44763   */
 44764  /* OP_OR: * * *
 44765   *
 44766   * Pop two values off the stack.  Take the logical OR of the
 44767   * two values and push the resulting boolean value back onto the
 44768   * stack. 
 44769   */
 44770  case JX9_OP_LAND:
 44771  case JX9_OP_LOR: {
 44772  	jx9_value *pNos = &pTos[-1];
 44773  	sxi32 v1, v2;    /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
 44774  #ifdef UNTRUST
 44775  	if( pNos < pStack ){
 44776  		goto Abort;
 44777  	}
 44778  #endif
 44779  	/* Force a boolean cast */
 44780  	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 44781  		jx9MemObjToBool(pTos);
 44782  	}
 44783  	if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
 44784  		jx9MemObjToBool(pNos);
 44785  	}
 44786  	v1 = pNos->x.iVal == 0 ? 1 : 0;
 44787  	v2 = pTos->x.iVal == 0 ? 1 : 0;
 44788  	if( pInstr->iOp == JX9_OP_LAND ){
 44789  		static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
 44790  		v1 = and_logic[v1*3+v2];
 44791  	}else{
 44792  		static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
 44793  		v1 = or_logic[v1*3+v2];
 44794  	}
 44795  	if( v1 == 2 ){
 44796  		v1 = 1;
 44797  	}
 44798  	VmPopOperand(&pTos, 1);
 44799  	pTos->x.iVal = v1 == 0 ? 1 : 0;
 44800  	MemObjSetType(pTos, MEMOBJ_BOOL);
 44801  	break;
 44802  				 }
 44803  /* OP_LXOR: * * *
 44804   *
 44805   * Pop two values off the stack. Take the logical XOR of the
 44806   * two values and push the resulting boolean value back onto the
 44807   * stack.
 44808   * According to the JX9 language reference manual:
 44809   *  $a xor $b is evaluated to TRUE if either $a or $b is 
 44810   *  TRUE, but not both.
 44811   */
 44812  case JX9_OP_LXOR:{
 44813  	jx9_value *pNos = &pTos[-1];
 44814  	sxi32 v = 0;
 44815  #ifdef UNTRUST
 44816  	if( pNos < pStack ){
 44817  		goto Abort;
 44818  	}
 44819  #endif
 44820  	/* Force a boolean cast */
 44821  	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
 44822  		jx9MemObjToBool(pTos);
 44823  	}
 44824  	if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
 44825  		jx9MemObjToBool(pNos);
 44826  	}
 44827  	if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){
 44828  		v = 1;
 44829  	}
 44830  	VmPopOperand(&pTos, 1);
 44831  	pTos->x.iVal = v;
 44832  	MemObjSetType(pTos, MEMOBJ_BOOL);
 44833  	break;
 44834  				 }
 44835  /* OP_EQ P1 P2 P3
 44836   *
 44837   * Pop the top two elements from the stack.  If they are equal, then
 44838   * jump to instruction P2.  Otherwise, continue to the next instruction.
 44839   * If P2 is zero, do not jump.  Instead, push a boolean 1 (TRUE) onto the
 44840   * stack if the jump would have been taken, or a 0 (FALSE) if not. 
 44841   */
 44842  /* OP_NEQ P1 P2 P3
 44843   *
 44844   * Pop the top two elements from the stack. If they are not equal, then
 44845   * jump to instruction P2. Otherwise, continue to the next instruction.
 44846   * If P2 is zero, do not jump.  Instead, push a boolean 1 (TRUE) onto the
 44847   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 44848   */
 44849  case JX9_OP_EQ:
 44850  case JX9_OP_NEQ: {
 44851  	jx9_value *pNos = &pTos[-1];
 44852  	/* Perform the comparison and act accordingly */
 44853  #ifdef UNTRUST
 44854  	if( pNos < pStack ){
 44855  		goto Abort;
 44856  	}
 44857  #endif
 44858  	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
 44859  	if( pInstr->iOp == JX9_OP_EQ ){
 44860  		rc = rc == 0;
 44861  	}else{
 44862  		rc = rc != 0;
 44863  	}
 44864  	VmPopOperand(&pTos, 1);
 44865  	if( !pInstr->iP2 ){
 44866  		/* Push comparison result without taking the jump */
 44867  		jx9MemObjRelease(pTos);
 44868  		pTos->x.iVal = rc;
 44869  		/* Invalidate any prior representation */
 44870  		MemObjSetType(pTos, MEMOBJ_BOOL);
 44871  	}else{
 44872  		if( rc ){
 44873  			/* Jump to the desired location */
 44874  			pc = pInstr->iP2 - 1;
 44875  			VmPopOperand(&pTos, 1);
 44876  		}
 44877  	}
 44878  	break;
 44879  				 }
 44880  /* OP_TEQ P1 P2 *
 44881   *
 44882   * Pop the top two elements from the stack. If they have the same type and are equal
 44883   * then jump to instruction P2. Otherwise, continue to the next instruction.
 44884   * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
 44885   * stack if the jump would have been taken, or a 0 (FALSE) if not. 
 44886   */
 44887  case JX9_OP_TEQ: {
 44888  	jx9_value *pNos = &pTos[-1];
 44889  	/* Perform the comparison and act accordingly */
 44890  #ifdef UNTRUST
 44891  	if( pNos < pStack ){
 44892  		goto Abort;
 44893  	}
 44894  #endif
 44895  	rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
 44896  	VmPopOperand(&pTos, 1);
 44897  	if( !pInstr->iP2 ){
 44898  		/* Push comparison result without taking the jump */
 44899  		jx9MemObjRelease(pTos);
 44900  		pTos->x.iVal = rc;
 44901  		/* Invalidate any prior representation */
 44902  		MemObjSetType(pTos, MEMOBJ_BOOL);
 44903  	}else{
 44904  		if( rc ){
 44905  			/* Jump to the desired location */
 44906  			pc = pInstr->iP2 - 1;
 44907  			VmPopOperand(&pTos, 1);
 44908  		}
 44909  	}
 44910  	break;
 44911  				 }
 44912  /* OP_TNE P1 P2 *
 44913   *
 44914   * Pop the top two elements from the stack.If they are not equal an they are not 
 44915   * of the same type, then jump to instruction P2. Otherwise, continue to the next 
 44916   * instruction.
 44917   * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
 44918   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 44919   * 
 44920   */
 44921  case JX9_OP_TNE: {
 44922  	jx9_value *pNos = &pTos[-1];
 44923  	/* Perform the comparison and act accordingly */
 44924  #ifdef UNTRUST
 44925  	if( pNos < pStack ){
 44926  		goto Abort;
 44927  	}
 44928  #endif
 44929  	rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
 44930  	VmPopOperand(&pTos, 1);
 44931  	if( !pInstr->iP2 ){
 44932  		/* Push comparison result without taking the jump */
 44933  		jx9MemObjRelease(pTos);
 44934  		pTos->x.iVal = rc;
 44935  		/* Invalidate any prior representation */
 44936  		MemObjSetType(pTos, MEMOBJ_BOOL);
 44937  	}else{
 44938  		if( rc ){
 44939  			/* Jump to the desired location */
 44940  			pc = pInstr->iP2 - 1;
 44941  			VmPopOperand(&pTos, 1);
 44942  		}
 44943  	}
 44944  	break;
 44945  				 }
 44946  /* OP_LT P1 P2 P3
 44947   *
 44948   * Pop the top two elements from the stack. If the second element (the top of stack)
 44949   * is less than the first (next on stack), then jump to instruction P2.Otherwise
 44950   * continue to the next instruction. In other words, jump if pNos<pTos.
 44951   * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
 44952   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 44953   * 
 44954   */
 44955  /* OP_LE P1 P2 P3
 44956   *
 44957   * Pop the top two elements from the stack. If the second element (the top of stack)
 44958   * is less than or equal to the first (next on stack), then jump to instruction P2.
 44959   * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
 44960   * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
 44961   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 44962   * 
 44963   */
 44964  case JX9_OP_LT:
 44965  case JX9_OP_LE: {
 44966  	jx9_value *pNos = &pTos[-1];
 44967  	/* Perform the comparison and act accordingly */
 44968  #ifdef UNTRUST
 44969  	if( pNos < pStack ){
 44970  		goto Abort;
 44971  	}
 44972  #endif
 44973  	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
 44974  	if( pInstr->iOp == JX9_OP_LE ){
 44975  		rc = rc < 1;
 44976  	}else{
 44977  		rc = rc < 0;
 44978  	}
 44979  	VmPopOperand(&pTos, 1);
 44980  	if( !pInstr->iP2 ){
 44981  		/* Push comparison result without taking the jump */
 44982  		jx9MemObjRelease(pTos);
 44983  		pTos->x.iVal = rc;
 44984  		/* Invalidate any prior representation */
 44985  		MemObjSetType(pTos, MEMOBJ_BOOL);
 44986  	}else{
 44987  		if( rc ){
 44988  			/* Jump to the desired location */
 44989  			pc = pInstr->iP2 - 1;
 44990  			VmPopOperand(&pTos, 1);
 44991  		}
 44992  	}
 44993  	break;
 44994  				}
 44995  /* OP_GT P1 P2 P3
 44996   *
 44997   * Pop the top two elements from the stack. If the second element (the top of stack)
 44998   * is greater than the first (next on stack), then jump to instruction P2.Otherwise
 44999   * continue to the next instruction. In other words, jump if pNos<pTos.
 45000   * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
 45001   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 45002   * 
 45003   */
 45004  /* OP_GE P1 P2 P3
 45005   *
 45006   * Pop the top two elements from the stack. If the second element (the top of stack)
 45007   * is greater than or equal to the first (next on stack), then jump to instruction P2.
 45008   * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
 45009   * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
 45010   * stack if the jump would have been taken, or a 0 (FALSE) if not.
 45011   * 
 45012   */
 45013  case JX9_OP_GT:
 45014  case JX9_OP_GE: {
 45015  	jx9_value *pNos = &pTos[-1];
 45016  	/* Perform the comparison and act accordingly */
 45017  #ifdef UNTRUST
 45018  	if( pNos < pStack ){
 45019  		goto Abort;
 45020  	}
 45021  #endif
 45022  	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
 45023  	if( pInstr->iOp == JX9_OP_GE ){
 45024  		rc = rc >= 0;
 45025  	}else{
 45026  		rc = rc > 0;
 45027  	}
 45028  	VmPopOperand(&pTos, 1);
 45029  	if( !pInstr->iP2 ){
 45030  		/* Push comparison result without taking the jump */
 45031  		jx9MemObjRelease(pTos);
 45032  		pTos->x.iVal = rc;
 45033  		/* Invalidate any prior representation */
 45034  		MemObjSetType(pTos, MEMOBJ_BOOL);
 45035  	}else{
 45036  		if( rc ){
 45037  			/* Jump to the desired location */
 45038  			pc = pInstr->iP2 - 1;
 45039  			VmPopOperand(&pTos, 1);
 45040  		}
 45041  	}
 45042  	break;
 45043  				}
 45044  /*
 45045   * OP_FOREACH_INIT * P2 P3
 45046   * Prepare a foreach step.
 45047   */
 45048  case JX9_OP_FOREACH_INIT: {
 45049  	jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
 45050  	void *pName;
 45051  #ifdef UNTRUST
 45052  	if( pTos < pStack ){
 45053  		goto Abort;
 45054  	}
 45055  #endif
 45056  	if( SyStringLength(&pInfo->sValue) < 1 ){
 45057  		/* Take the variable name from the top of the stack */
 45058  		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 45059  			/* Force a string cast */
 45060  			jx9MemObjToString(pTos);
 45061  		}
 45062  		/* Duplicate name */
 45063  		if( SyBlobLength(&pTos->sBlob) > 0 ){
 45064  			pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 45065  			SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob));
 45066  		}
 45067  		VmPopOperand(&pTos, 1);
 45068  	}
 45069  	if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
 45070  		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 45071  			/* Force a string cast */
 45072  			jx9MemObjToString(pTos);
 45073  		}
 45074  		/* Duplicate name */
 45075  		if( SyBlobLength(&pTos->sBlob) > 0 ){
 45076  			pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 45077  			SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob));
 45078  		}
 45079  		VmPopOperand(&pTos, 1);
 45080  	}
 45081  	/* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/
 45082  	if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
 45083  		/* Jump out of the loop */
 45084  		if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
 45085  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
 45086  				"Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
 45087  		}
 45088  		pc = pInstr->iP2 - 1;
 45089  	}else{
 45090  		jx9_foreach_step *pStep;
 45091  		pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
 45092  		if( pStep == 0 ){
 45093  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
 45094  			/* Jump out of the loop */
 45095  			pc = pInstr->iP2 - 1;
 45096  		}else{
 45097  			/* Zero the structure */
 45098  			SyZero(pStep, sizeof(jx9_foreach_step));
 45099  			/* Prepare the step */
 45100  			pStep->iFlags = pInfo->iFlags;
 45101  			if( pTos->iFlags & MEMOBJ_HASHMAP ){
 45102  				jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
 45103  				/* Reset the internal loop cursor */
 45104  				jx9HashmapResetLoopCursor(pMap);
 45105  				/* Mark the step */
 45106  				pStep->pMap = pMap;
 45107  				pMap->iRef++;
 45108  			}
 45109  		}
 45110  		if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
 45111  			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
 45112  			SyMemBackendPoolFree(&pVm->sAllocator, pStep);
 45113  			/* Jump out of the loop */
 45114  			pc = pInstr->iP2 - 1;
 45115  		}
 45116  	}
 45117  	VmPopOperand(&pTos, 1);
 45118  	break;
 45119  						  }
 45120  /*
 45121   * OP_FOREACH_STEP * P2 P3
 45122   * Perform a foreach step. Jump to P2 at the end of the step.
 45123   */
 45124  case JX9_OP_FOREACH_STEP: {
 45125  	jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
 45126  	jx9_foreach_step **apStep, *pStep;
 45127  	jx9_hashmap_node *pNode;
 45128  	jx9_hashmap *pMap;
 45129  	jx9_value *pValue;
 45130  	/* Peek the last step */
 45131  	apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
 45132  	pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
 45133  	pMap = pStep->pMap;
 45134  	/* Extract the current node value */
 45135  	pNode = jx9HashmapGetNextEntry(pMap);
 45136  	if( pNode == 0 ){
 45137  		/* No more entry to process */
 45138  		pc = pInstr->iP2 - 1; /* Jump to this destination */
 45139  		/* Automatically reset the loop cursor */
 45140  		jx9HashmapResetLoopCursor(pMap);
 45141  		/* Cleanup the mess left behind */
 45142  		SyMemBackendPoolFree(&pVm->sAllocator, pStep);
 45143  		SySetPop(&pInfo->aStep);
 45144  		jx9HashmapUnref(pMap);
 45145  	}else{
 45146  		if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
 45147  			jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
 45148  			if( pKey ){
 45149  				jx9HashmapExtractNodeKey(pNode, pKey);
 45150  			}
 45151  		}
 45152  		/* Make a copy of the entry value */
 45153  		pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
 45154  		if( pValue ){
 45155  			jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
 45156  		}
 45157  	}
 45158  	break;
 45159  						  }
 45160  /*
 45161   * OP_MEMBER P1 P2
 45162   * Load JSON object entry on the stack.
 45163   */
 45164  case JX9_OP_MEMBER: {
 45165  	jx9_hashmap_node *pNode = 0; /* cc warning */
 45166  	jx9_hashmap *pMap = 0;
 45167  	jx9_value *pIdx;
 45168  	pIdx = pTos;
 45169  	pTos--;
 45170  	rc = SXERR_NOTFOUND; /* Assume the index is invalid */
 45171  	if( pTos->iFlags & MEMOBJ_HASHMAP ){
 45172  		/* Point to the hashmap */
 45173  		pMap = (jx9_hashmap *)pTos->x.pOther;
 45174  		/* Load the desired entry */
 45175  		rc = jx9HashmapLookup(pMap, pIdx, &pNode);
 45176  	}
 45177  	jx9MemObjRelease(pIdx);	
 45178  	if( rc == SXRET_OK ){
 45179  		/* Load entry contents */
 45180  		if( pMap->iRef < 2 ){
 45181  			/* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
 45182  			 * of the entry value, rather than pointing to it.
 45183  			 */
 45184  			pTos->nIdx = SXU32_HIGH;
 45185  			jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
 45186  		}else{
 45187  			pTos->nIdx = pNode->nValIdx;
 45188  			jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
 45189  			jx9HashmapUnref(pMap);
 45190  		}
 45191  	}else{
 45192  		/* No such entry, load NULL */
 45193  		jx9MemObjRelease(pTos);
 45194  		pTos->nIdx = SXU32_HIGH;
 45195  	}
 45196  	break;
 45197  					}
 45198  /*
 45199   * OP_SWITCH * * P3
 45200   *  This is the bytecode implementation of the complex switch() JX9 construct.
 45201   */
 45202  case JX9_OP_SWITCH: {
 45203  	jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
 45204  	jx9_case_expr *aCase, *pCase;
 45205  	jx9_value sValue, sCaseValue; 
 45206  	sxu32 n, nEntry;
 45207  #ifdef UNTRUST
 45208  	if( pSwitch == 0 || pTos < pStack ){
 45209  		goto Abort;
 45210  	}
 45211  #endif
 45212  	/* Point to the case table  */
 45213  	aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
 45214  	nEntry = SySetUsed(&pSwitch->aCaseExpr);
 45215  	/* Select the appropriate case block to execute */
 45216  	jx9MemObjInit(pVm, &sValue);
 45217  	jx9MemObjInit(pVm, &sCaseValue);
 45218  	for( n = 0 ; n < nEntry ; ++n ){
 45219  		pCase = &aCase[n];
 45220  		jx9MemObjLoad(pTos, &sValue);
 45221  		/* Execute the case expression first */
 45222  		VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
 45223  		/* Compare the two expression */
 45224  		rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
 45225  		jx9MemObjRelease(&sValue);
 45226  		jx9MemObjRelease(&sCaseValue);
 45227  		if( rc == 0 ){
 45228  			/* Value match, jump to this block */
 45229  			pc = pCase->nStart - 1;
 45230  			break;
 45231  		}
 45232  	}
 45233  	VmPopOperand(&pTos, 1);
 45234  	if( n >= nEntry ){
 45235  		/* No approprite case to execute, jump to the default case */
 45236  		if( pSwitch->nDefault > 0 ){
 45237  			pc = pSwitch->nDefault - 1;
 45238  		}else{
 45239  			/* No default case, jump out of this switch */
 45240  			pc = pSwitch->nOut - 1;
 45241  		}
 45242  	}
 45243  	break;
 45244  					}
 45245  /*
 45246   * OP_UPLINK P1 * *
 45247   * Link a variable to the top active VM frame. 
 45248   * This is used to implement the 'uplink' JX9 construct.
 45249   */
 45250  case JX9_OP_UPLINK: {
 45251  	if( pVm->pFrame->pParent ){
 45252  		jx9_value *pLink = &pTos[-pInstr->iP1+1];
 45253  		SyString sName;
 45254  		/* Perform the link */
 45255  		while( pLink <= pTos ){
 45256  			if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
 45257  				/* Force a string cast */
 45258  				jx9MemObjToString(pLink);
 45259  			}
 45260  			SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
 45261  			if( sName.nByte > 0 ){
 45262  				VmFrameLink(&(*pVm), &sName);
 45263  			}
 45264  			pLink++;
 45265  		}
 45266  	}
 45267  	VmPopOperand(&pTos, pInstr->iP1);
 45268  	break;
 45269  					}
 45270  /*
 45271   * OP_CALL P1 * *
 45272   *  Call a JX9 or a foreign function and push the return value of the called
 45273   *  function on the stack.
 45274   */
 45275  case JX9_OP_CALL: {
 45276  	jx9_value *pArg = &pTos[-pInstr->iP1];
 45277  	SyHashEntry *pEntry;
 45278  	SyString sName;
 45279  	/* Extract function name */
 45280  	if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
 45281  		/* Raise exception: Invalid function name */
 45282  		VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
 45283  		/* Pop given arguments */
 45284  		if( pInstr->iP1 > 0 ){
 45285  			VmPopOperand(&pTos, pInstr->iP1);
 45286  		}
 45287  		/* Assume a null return value so that the program continue it's execution normally */
 45288  		jx9MemObjRelease(pTos);
 45289  		break;
 45290  	}
 45291  	SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
 45292  	/* Check for a compiled function first */
 45293  	pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte);
 45294  	if( pEntry ){
 45295  		jx9_vm_func_arg *aFormalArg;
 45296  		jx9_value *pFrameStack;
 45297  		jx9_vm_func *pVmFunc;
 45298  		VmFrame *pFrame;
 45299  		jx9_value *pObj;
 45300  		VmSlot sArg;
 45301  		sxu32 n;
 45302  		pVmFunc = (jx9_vm_func *)pEntry->pUserData;
 45303  		/* Check The recursion limit */
 45304  		if( pVm->nRecursionDepth > pVm->nMaxDepth ){
 45305  			VmErrorFormat(&(*pVm), JX9_CTX_ERR, 
 45306  				"Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value", 
 45307  				&pVmFunc->sName);
 45308  			/* Pop given arguments */
 45309  			if( pInstr->iP1 > 0 ){
 45310  				VmPopOperand(&pTos, pInstr->iP1);
 45311  			}
 45312  			/* Assume a null return value so that the program continue it's execution normally */
 45313  			jx9MemObjRelease(pTos);
 45314  			break;
 45315  		}
 45316  		if( pVmFunc->pNextName ){
 45317  			/* Function is candidate for overloading, select the appropriate function to call */
 45318  			pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg));
 45319  		}
 45320  		/* Extract the formal argument set */
 45321  		aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
 45322  		/* Create a new VM frame  */
 45323  		rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
 45324  		if( rc != SXRET_OK ){
 45325  			/* Raise exception: Out of memory */
 45326  			VmErrorFormat(&(*pVm), JX9_CTX_ERR, 
 45327  				"JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", 
 45328  				&pVmFunc->sName);
 45329  			/* Pop given arguments */
 45330  			if( pInstr->iP1 > 0 ){
 45331  				VmPopOperand(&pTos, pInstr->iP1);
 45332  			}
 45333  			/* Assume a null return value so that the program continue it's execution normally */
 45334  			jx9MemObjRelease(pTos);
 45335  			break;
 45336  		}
 45337  		if( SySetUsed(&pVmFunc->aStatic) > 0 ){
 45338  			jx9_vm_func_static_var *pStatic, *aStatic;
 45339  			/* Install static variables */
 45340  			aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic);
 45341  			for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){
 45342  				pStatic = &aStatic[n];
 45343  				if( pStatic->nIdx == SXU32_HIGH ){
 45344  					/* Initialize the static variables */
 45345  					pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx);
 45346  					if( pObj ){
 45347  						/* Assume a NULL initialization value */
 45348  						jx9MemObjInit(&(*pVm), pObj);
 45349  						if( SySetUsed(&pStatic->aByteCode) > 0 ){
 45350  							/* Evaluate initialization expression (Any complex expression) */
 45351  							VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj);
 45352  						}
 45353  						pObj->nIdx = pStatic->nIdx;
 45354  					}else{
 45355  						continue;
 45356  					}
 45357  				}
 45358  				/* Install in the current frame */
 45359  				SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName), 
 45360  					SX_INT_TO_PTR(pStatic->nIdx));
 45361  			}
 45362  		}
 45363  		/* Push arguments in the local frame */
 45364  		n = 0;
 45365  		while( pArg < pTos ){
 45366  			if( n < SySetUsed(&pVmFunc->aArgs) ){
 45367  				if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
 45368  					/* NULL values are redirected to default arguments */
 45369  					rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg);
 45370  					if( rc == JX9_ABORT ){
 45371  						goto Abort;
 45372  					}
 45373  				}
 45374  				/* Make sure the given arguments are of the correct type */
 45375  				if( aFormalArg[n].nType > 0 ){
 45376  				 if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
 45377  						ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
 45378  						/* Cast to the desired type */
 45379  						if( xCast ){
 45380  							xCast(pArg);
 45381  						}
 45382  					}
 45383  				}
 45384  				/* Pass by value, make a copy of the given argument */
 45385  				pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
 45386  			}else{
 45387  				char zName[32];
 45388  				SyString sName;
 45389  				/* Set a dummy name */
 45390  				sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n);
 45391  				sName.zString = zName;
 45392  				/* Annonymous argument */
 45393  				pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE);
 45394  			}
 45395  			if( pObj ){
 45396  				jx9MemObjStore(pArg, pObj);
 45397  				/* Insert argument index  */
 45398  				sArg.nIdx = pObj->nIdx;
 45399  				sArg.pUserData = 0;
 45400  				SySetPut(&pFrame->sArg, (const void *)&sArg);
 45401  			}
 45402  			jx9MemObjRelease(pArg);
 45403  			pArg++;
 45404  			++n;
 45405  		}
 45406  		/* Process default values */
 45407  		while( n < SySetUsed(&pVmFunc->aArgs) ){
 45408  			if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
 45409  				pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
 45410  				if( pObj ){
 45411  					/* Evaluate the default value and extract it's result */
 45412  					rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
 45413  					if( rc == JX9_ABORT ){
 45414  						goto Abort;
 45415  					}
 45416  					/* Insert argument index */
 45417  					sArg.nIdx = pObj->nIdx;
 45418  					sArg.pUserData = 0;
 45419  					SySetPut(&pFrame->sArg, (const void *)&sArg);
 45420  					/* Make sure the default argument is of the correct type */
 45421  					if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){
 45422  						ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
 45423  						/* Cast to the desired type */
 45424  						xCast(pObj);
 45425  					}
 45426  				}
 45427  			}
 45428  			++n;
 45429  		}
 45430  		/* Pop arguments, function name from the operand stack and assume the function 
 45431  		 * does not return anything.
 45432  		 */
 45433  		jx9MemObjRelease(pTos);
 45434  		pTos = &pTos[-pInstr->iP1];
 45435  		/* Allocate a new operand stack and evaluate the function body */
 45436  		pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode));
 45437  		if( pFrameStack == 0 ){
 45438  			/* Raise exception: Out of memory */
 45439  			VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.", 
 45440  				&pVmFunc->sName);
 45441  			if( pInstr->iP1 > 0 ){
 45442  				VmPopOperand(&pTos, pInstr->iP1);
 45443  			}
 45444  			break;
 45445  		}
 45446  		/* Increment nesting level */
 45447  		pVm->nRecursionDepth++;
 45448  		/* Execute function body */
 45449  		rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
 45450  		/* Decrement nesting level */
 45451  		pVm->nRecursionDepth--;
 45452  		/* Free the operand stack */
 45453  		SyMemBackendFree(&pVm->sAllocator, pFrameStack);
 45454  		/* Leave the frame */
 45455  		VmLeaveFrame(&(*pVm));
 45456  		if( rc == JX9_ABORT ){
 45457  			/* Abort processing immeditaley */
 45458  			goto Abort;
 45459  		}
 45460  	}else{
 45461  		jx9_user_func *pFunc; 
 45462  		jx9_context sCtx;
 45463  		jx9_value sRet;
 45464  		/* Look for an installed foreign function */
 45465  		pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte);
 45466  		if( pEntry == 0 ){
 45467  			/* Call to undefined function */
 45468  			VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName);
 45469  			/* Pop given arguments */
 45470  			if( pInstr->iP1 > 0 ){
 45471  				VmPopOperand(&pTos, pInstr->iP1);
 45472  			}
 45473  			/* Assume a null return value so that the program continue it's execution normally */
 45474  			jx9MemObjRelease(pTos);
 45475  			break;
 45476  		}
 45477  		pFunc = (jx9_user_func *)pEntry->pUserData;
 45478  		/* Start collecting function arguments */
 45479  		SySetReset(&aArg);
 45480  		while( pArg < pTos ){
 45481  			SySetPut(&aArg, (const void *)&pArg);
 45482  			pArg++;
 45483  		}
 45484  		/* Assume a null return value */
 45485  		jx9MemObjInit(&(*pVm), &sRet);
 45486  		/* Init the call context */
 45487  		VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
 45488  		/* Call the foreign function */
 45489  		rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
 45490  		/* Release the call context */
 45491  		VmReleaseCallContext(&sCtx);
 45492  		if( rc == JX9_ABORT ){
 45493  			goto Abort;
 45494  		}
 45495  		if( pInstr->iP1 > 0 ){
 45496  			/* Pop function name and arguments */
 45497  			VmPopOperand(&pTos, pInstr->iP1);
 45498  		}
 45499  		/* Save foreign function return value */
 45500  		jx9MemObjStore(&sRet, pTos);
 45501  		jx9MemObjRelease(&sRet);
 45502  	}
 45503  	break;
 45504  				  }
 45505  /*
 45506   * OP_CONSUME: P1 * *
 45507   * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
 45508   */
 45509  case JX9_OP_CONSUME: {
 45510  	jx9_output_consumer *pCons = &pVm->sVmConsumer;
 45511  	jx9_value *pCur, *pOut = pTos;
 45512  
 45513  	pOut = &pTos[-pInstr->iP1 + 1];
 45514  	pCur = pOut;
 45515  	/* Start the consume process  */
 45516  	while( pOut <= pTos ){
 45517  		/* Force a string cast */
 45518  		if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){
 45519  			jx9MemObjToString(pOut);
 45520  		}
 45521  		if( SyBlobLength(&pOut->sBlob) > 0 ){
 45522  			/*SyBlobNullAppend(&pOut->sBlob);*/
 45523  			/* Invoke the output consumer callback */
 45524  			rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData);
 45525  			/* Increment output length */
 45526  			pVm->nOutputLen += SyBlobLength(&pOut->sBlob);
 45527  			SyBlobRelease(&pOut->sBlob);
 45528  			if( rc == SXERR_ABORT ){
 45529  				/* Output consumer callback request an operation abort. */
 45530  				goto Abort;
 45531  			}
 45532  		}
 45533  		pOut++;
 45534  	}
 45535  	pTos = &pCur[-1];
 45536  	break;
 45537  					 }
 45538  
 45539  		} /* Switch() */
 45540  		pc++; /* Next instruction in the stream */
 45541  	} /* For(;;) */
 45542  Done:
 45543  	SySetRelease(&aArg);
 45544  	return SXRET_OK;
 45545  Abort:
 45546  	SySetRelease(&aArg);
 45547  	while( pTos >= pStack ){
 45548  		jx9MemObjRelease(pTos);
 45549  		pTos--;
 45550  	}
 45551  	return JX9_ABORT;
 45552  }
 45553  /*
 45554   * Execute as much of a local JX9 bytecode program as we can then return.
 45555   * This function is a wrapper around [VmByteCodeExec()].
 45556   * See block-comment on that function for additional information.
 45557   */
 45558  static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
 45559  {
 45560  	jx9_value *pStack;
 45561  	sxi32 rc;
 45562  	/* Allocate a new operand stack */
 45563  	pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode));
 45564  	if( pStack == 0 ){
 45565  		return SXERR_MEM;
 45566  	}
 45567  	/* Execute the program */
 45568  	rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult));
 45569  	/* Free the operand stack */
 45570  	SyMemBackendFree(&pVm->sAllocator, pStack);
 45571  	/* Execution result */
 45572  	return rc;
 45573  }
 45574  /*
 45575   * Execute as much of a JX9 bytecode program as we can then return.
 45576   * This function is a wrapper around [VmByteCodeExec()].
 45577   * See block-comment on that function for additional information.
 45578   */
 45579  JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
 45580  {
 45581  	/* Make sure we are ready to execute this program */
 45582  	if( pVm->nMagic != JX9_VM_RUN ){
 45583  		return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
 45584  	}
 45585  	/* Set the execution magic number  */
 45586  	pVm->nMagic = JX9_VM_EXEC;
 45587  	/* Execute the program */
 45588  	VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
 45589  	/*
 45590  	 * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
 45591  	 * so that any following call to [jx9_vm_exec()] without calling
 45592  	 * [jx9_vm_reset()] first would fail.
 45593  	 */
 45594  	return SXRET_OK;
 45595  }
 45596  /*
 45597   * Extract a memory object (i.e. a variable) from the running script.
 45598   * This function must be called after calling jx9_vm_exec(). Otherwise
 45599   * NULL is returned.
 45600   */
 45601  JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
 45602  {
 45603  	jx9_value *pValue;
 45604  	if( pVm->nMagic != JX9_VM_EXEC ){
 45605  		/* call jx9_vm_exec() first */
 45606  		return 0;
 45607  	}
 45608  	/* Perform the lookup */
 45609  	pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
 45610  	/* Lookup result */
 45611  	return pValue;
 45612  }
 45613  /*
 45614   * Invoke the installed VM output consumer callback to consume
 45615   * the desired message.
 45616   * Refer to the implementation of [jx9_context_output()] defined
 45617   * in 'api.c' for additional information.
 45618   */
 45619  JX9_PRIVATE sxi32 jx9VmOutputConsume(
 45620  	jx9_vm *pVm,      /* Target VM */
 45621  	SyString *pString /* Message to output */
 45622  	)
 45623  {
 45624  	jx9_output_consumer *pCons = &pVm->sVmConsumer;
 45625  	sxi32 rc = SXRET_OK;
 45626  	/* Call the output consumer */
 45627  	if( pString->nByte > 0 ){
 45628  		rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData);
 45629  		/* Increment output length */
 45630  		pVm->nOutputLen += pString->nByte;
 45631  	}
 45632  	return rc;
 45633  }
 45634  /*
 45635   * Format a message and invoke the installed VM output consumer
 45636   * callback to consume the formatted message.
 45637   * Refer to the implementation of [jx9_context_output_format()] defined
 45638   * in 'api.c' for additional information.
 45639   */
 45640  JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
 45641  	jx9_vm *pVm,         /* Target VM */
 45642  	const char *zFormat, /* Formatted message to output */
 45643  	va_list ap           /* Variable list of arguments */ 
 45644  	)
 45645  {
 45646  	jx9_output_consumer *pCons = &pVm->sVmConsumer;
 45647  	sxi32 rc = SXRET_OK;
 45648  	SyBlob sWorker;
 45649  	/* Format the message and call the output consumer */
 45650  	SyBlobInit(&sWorker, &pVm->sAllocator);
 45651  	SyBlobFormatAp(&sWorker, zFormat, ap);
 45652  	if( SyBlobLength(&sWorker) > 0 ){
 45653  		/* Consume the formatted message */
 45654  		rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData);
 45655  	}
 45656  	/* Increment output length */
 45657  	pVm->nOutputLen += SyBlobLength(&sWorker);
 45658  	/* Release the working buffer */
 45659  	SyBlobRelease(&sWorker);
 45660  	return rc;
 45661  }
 45662  /*
 45663   * Return a string representation of the given JX9 OP code.
 45664   * This function never fail and always return a pointer
 45665   * to a null terminated string.
 45666   */
 45667  static const char * VmInstrToString(sxi32 nOp)
 45668  {
 45669  	const char *zOp = "Unknown     ";
 45670  	switch(nOp){
 45671  	case JX9_OP_DONE:       zOp = "DONE       "; break;
 45672  	case JX9_OP_HALT:       zOp = "HALT       "; break;
 45673  	case JX9_OP_LOAD:       zOp = "LOAD       "; break;
 45674  	case JX9_OP_LOADC:      zOp = "LOADC      "; break;
 45675  	case JX9_OP_LOAD_MAP:   zOp = "LOAD_MAP   "; break;
 45676  	case JX9_OP_LOAD_IDX:   zOp = "LOAD_IDX   "; break;
 45677  	case JX9_OP_NOOP:       zOp = "NOOP       "; break;
 45678  	case JX9_OP_JMP:        zOp = "JMP        "; break;
 45679  	case JX9_OP_JZ:         zOp = "JZ         "; break;
 45680  	case JX9_OP_JNZ:        zOp = "JNZ        "; break;
 45681  	case JX9_OP_POP:        zOp = "POP        "; break;
 45682  	case JX9_OP_CAT:        zOp = "CAT        "; break;
 45683  	case JX9_OP_CVT_INT:    zOp = "CVT_INT    "; break;
 45684  	case JX9_OP_CVT_STR:    zOp = "CVT_STR    "; break;
 45685  	case JX9_OP_CVT_REAL:   zOp = "CVT_REAL   "; break;
 45686  	case JX9_OP_CALL:       zOp = "CALL       "; break;
 45687  	case JX9_OP_UMINUS:     zOp = "UMINUS     "; break;
 45688  	case JX9_OP_UPLUS:      zOp = "UPLUS      "; break;
 45689  	case JX9_OP_BITNOT:     zOp = "BITNOT     "; break;
 45690  	case JX9_OP_LNOT:       zOp = "LOGNOT     "; break;
 45691  	case JX9_OP_MUL:        zOp = "MUL        "; break;
 45692  	case JX9_OP_DIV:        zOp = "DIV        "; break;
 45693  	case JX9_OP_MOD:        zOp = "MOD        "; break;
 45694  	case JX9_OP_ADD:        zOp = "ADD        "; break;
 45695  	case JX9_OP_SUB:        zOp = "SUB        "; break;
 45696  	case JX9_OP_SHL:        zOp = "SHL        "; break;
 45697  	case JX9_OP_SHR:        zOp = "SHR        "; break;
 45698  	case JX9_OP_LT:         zOp = "LT         "; break;
 45699  	case JX9_OP_LE:         zOp = "LE         "; break;
 45700  	case JX9_OP_GT:         zOp = "GT         "; break;
 45701  	case JX9_OP_GE:         zOp = "GE         "; break;
 45702  	case JX9_OP_EQ:         zOp = "EQ         "; break;
 45703  	case JX9_OP_NEQ:        zOp = "NEQ        "; break;
 45704  	case JX9_OP_TEQ:        zOp = "TEQ        "; break;
 45705  	case JX9_OP_TNE:        zOp = "TNE        "; break;
 45706  	case JX9_OP_BAND:       zOp = "BITAND     "; break;
 45707  	case JX9_OP_BXOR:       zOp = "BITXOR     "; break;
 45708  	case JX9_OP_BOR:        zOp = "BITOR      "; break;
 45709  	case JX9_OP_LAND:       zOp = "LOGAND     "; break;
 45710  	case JX9_OP_LOR:        zOp = "LOGOR      "; break;
 45711  	case JX9_OP_LXOR:       zOp = "LOGXOR     "; break;
 45712  	case JX9_OP_STORE:      zOp = "STORE      "; break;
 45713  	case JX9_OP_STORE_IDX:  zOp = "STORE_IDX  "; break;
 45714  	case JX9_OP_PULL:       zOp = "PULL       "; break;
 45715  	case JX9_OP_SWAP:       zOp = "SWAP       "; break;
 45716  	case JX9_OP_YIELD:      zOp = "YIELD      "; break;
 45717  	case JX9_OP_CVT_BOOL:   zOp = "CVT_BOOL   "; break;
 45718  	case JX9_OP_CVT_NULL:   zOp = "CVT_NULL   "; break;
 45719  	case JX9_OP_CVT_ARRAY:  zOp = "CVT_JSON   "; break;
 45720  	case JX9_OP_CVT_NUMC:   zOp = "CVT_NUMC   "; break;
 45721  	case JX9_OP_INCR:       zOp = "INCR       "; break;
 45722  	case JX9_OP_DECR:       zOp = "DECR       "; break;
 45723  	case JX9_OP_ADD_STORE:  zOp = "ADD_STORE  "; break;
 45724  	case JX9_OP_SUB_STORE:  zOp = "SUB_STORE  "; break;
 45725  	case JX9_OP_MUL_STORE:  zOp = "MUL_STORE  "; break;
 45726  	case JX9_OP_DIV_STORE:  zOp = "DIV_STORE  "; break;
 45727  	case JX9_OP_MOD_STORE:  zOp = "MOD_STORE  "; break;
 45728  	case JX9_OP_CAT_STORE:  zOp = "CAT_STORE  "; break;
 45729  	case JX9_OP_SHL_STORE:  zOp = "SHL_STORE  "; break;
 45730  	case JX9_OP_SHR_STORE:  zOp = "SHR_STORE  "; break;
 45731  	case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
 45732  	case JX9_OP_BOR_STORE:  zOp = "BOR_STORE  "; break;
 45733  	case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
 45734  	case JX9_OP_CONSUME:    zOp = "CONSUME    "; break;
 45735  	case JX9_OP_MEMBER:     zOp = "MEMBER     "; break;
 45736  	case JX9_OP_UPLINK:     zOp = "UPLINK     "; break;
 45737  	case JX9_OP_SWITCH:     zOp = "SWITCH     "; break;
 45738  	case JX9_OP_FOREACH_INIT:
 45739  		                    zOp = "4EACH_INIT "; break;
 45740  	case JX9_OP_FOREACH_STEP:
 45741  						    zOp = "4EACH_STEP "; break;
 45742  	default:
 45743  		break;
 45744  	}
 45745  	return zOp;
 45746  }
 45747  /*
 45748   * Dump JX9 bytecodes instructions to a human readable format.
 45749   * The xConsumer() callback which is an used defined function
 45750   * is responsible of consuming the generated dump.
 45751   */
 45752  JX9_PRIVATE sxi32 jx9VmDump(
 45753  	jx9_vm *pVm,            /* Target VM */
 45754  	ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
 45755  	void *pUserData         /* Last argument to xConsumer() */
 45756  	)
 45757  {
 45758  	sxi32 rc;
 45759  	rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData);
 45760  	return rc;
 45761  }
 45762  /*
 45763   * Default constant expansion callback used by the 'const' statement if used
 45764   * outside a object body [i.e: global or function scope].
 45765   * Refer to the implementation of [JX9_CompileConstant()] defined
 45766   * in 'compile.c' for additional information.
 45767   */
 45768  JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
 45769  {
 45770  	SySet *pByteCode = (SySet *)pUserData;
 45771  	/* Evaluate and expand constant value */
 45772  	VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
 45773  }
 45774  /*
 45775   * Section:
 45776   *  Function handling functions.
 45777   * Authors:
 45778   *    Symisc Systems, devel@symisc.net.
 45779   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 45780   * Status:
 45781   *    Stable.
 45782   */
 45783  /*
 45784   * int func_num_args(void)
 45785   *   Returns the number of arguments passed to the function.
 45786   * Parameters
 45787   *   None.
 45788   * Return
 45789   *  Total number of arguments passed into the current user-defined function
 45790   *  or -1 if called from the globe scope.
 45791   */
 45792  static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
 45793  {
 45794  	VmFrame *pFrame;
 45795  	jx9_vm *pVm;
 45796  	/* Point to the target VM */
 45797  	pVm = pCtx->pVm;
 45798  	/* Current frame */
 45799  	pFrame = pVm->pFrame;
 45800  	if( pFrame->pParent == 0 ){
 45801  		SXUNUSED(nArg);
 45802  		SXUNUSED(apArg);
 45803  		/* Global frame, return -1 */
 45804  		jx9_result_int(pCtx, -1);
 45805  		return SXRET_OK;
 45806  	}
 45807  	/* Total number of arguments passed to the enclosing function */
 45808  	nArg = (int)SySetUsed(&pFrame->sArg);
 45809  	jx9_result_int(pCtx, nArg);
 45810  	return SXRET_OK;
 45811  }
 45812  /*
 45813   * value func_get_arg(int $arg_num)
 45814   *   Return an item from the argument list.
 45815   * Parameters
 45816   *  Argument number(index start from zero).
 45817   * Return
 45818   *  Returns the specified argument or FALSE on error.
 45819   */
 45820  static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg)
 45821  {
 45822  	jx9_value *pObj = 0;
 45823  	VmSlot *pSlot = 0;
 45824  	VmFrame *pFrame;
 45825  	jx9_vm *pVm;
 45826  	/* Point to the target VM */
 45827  	pVm = pCtx->pVm;
 45828  	/* Current frame */
 45829  	pFrame = pVm->pFrame;
 45830  	if( nArg < 1 || pFrame->pParent == 0 ){
 45831  		/* Global frame or Missing arguments, return FALSE */
 45832  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
 45833  		jx9_result_bool(pCtx, 0);
 45834  		return SXRET_OK;
 45835  	}
 45836  	/* Extract the desired index */
 45837  	nArg = jx9_value_to_int(apArg[0]);
 45838  	if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
 45839  		/* Invalid index, return FALSE */
 45840  		jx9_result_bool(pCtx, 0);
 45841  		return SXRET_OK;
 45842  	}
 45843  	/* Extract the desired argument */
 45844  	if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
 45845  		if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
 45846  			/* Return the desired argument */
 45847  			jx9_result_value(pCtx, (jx9_value *)pObj);
 45848  		}else{
 45849  			/* No such argument, return false */
 45850  			jx9_result_bool(pCtx, 0);
 45851  		}
 45852  	}else{
 45853  		/* CAN'T HAPPEN */
 45854  		jx9_result_bool(pCtx, 0);
 45855  	}
 45856  	return SXRET_OK;
 45857  }
 45858  /*
 45859   * array func_get_args(void)
 45860   *   Returns an array comprising a copy of function's argument list.
 45861   * Parameters
 45862   *  None.
 45863   * Return
 45864   *  Returns an array in which each element is a copy of the corresponding
 45865   *  member of the current user-defined function's argument list.
 45866   *  Otherwise FALSE is returned on failure.
 45867   */
 45868  static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
 45869  {
 45870  	jx9_value *pObj = 0;
 45871  	jx9_value *pArray;
 45872  	VmFrame *pFrame;
 45873  	VmSlot *aSlot;
 45874  	sxu32 n;
 45875  	/* Point to the current frame */
 45876  	pFrame = pCtx->pVm->pFrame;
 45877  	if( pFrame->pParent == 0 ){
 45878  		/* Global frame, return FALSE */
 45879  		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
 45880  		jx9_result_bool(pCtx, 0);
 45881  		return SXRET_OK;
 45882  	}
 45883  	/* Create a new array */
 45884  	pArray = jx9_context_new_array(pCtx);
 45885  	if( pArray == 0 ){
 45886  		SXUNUSED(nArg); /* cc warning */
 45887  		SXUNUSED(apArg);
 45888  		jx9_result_bool(pCtx, 0);
 45889  		return SXRET_OK;
 45890  	}
 45891  	/* Start filling the array with the given arguments */
 45892  	aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
 45893  	for( n = 0;  n < SySetUsed(&pFrame->sArg) ; n++ ){
 45894  		pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
 45895  		if( pObj ){
 45896  			jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
 45897  		}
 45898  	}
 45899  	/* Return the freshly created array */
 45900  	jx9_result_value(pCtx, pArray);
 45901  	return SXRET_OK;
 45902  }
 45903  /*
 45904   * bool function_exists(string $name)
 45905   *  Return TRUE if the given function has been defined.
 45906   * Parameters
 45907   *  The name of the desired function.
 45908   * Return
 45909   *  Return TRUE if the given function has been defined.False otherwise
 45910   */
 45911  static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
 45912  {
 45913  	const char *zName;
 45914  	jx9_vm *pVm;
 45915  	int nLen;
 45916  	int res;
 45917  	if( nArg < 1 ){
 45918  		/* Missing argument, return FALSE */
 45919  		jx9_result_bool(pCtx, 0);
 45920  		return SXRET_OK;
 45921  	}
 45922  	/* Point to the target VM */
 45923  	pVm = pCtx->pVm;
 45924  	/* Extract the function name */
 45925  	zName = jx9_value_to_string(apArg[0], &nLen);
 45926  	/* Assume the function is not defined */
 45927  	res = 0;
 45928  	/* Perform the lookup */
 45929  	if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
 45930  		SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
 45931  			/* Function is defined */
 45932  			res = 1;
 45933  	}
 45934  	jx9_result_bool(pCtx, res);
 45935  	return SXRET_OK;
 45936  }
 45937  /*
 45938   * Verify that the contents of a variable can be called as a function.
 45939   * [i.e: Whether it is callable or not].
 45940   * Return TRUE if callable.FALSE otherwise.
 45941   */
 45942  JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
 45943  {
 45944  	int res = 0;
 45945  	if( pValue->iFlags & MEMOBJ_STRING ){
 45946  		const char *zName;
 45947  		int nLen;
 45948  		/* Extract the name */
 45949  		zName = jx9_value_to_string(pValue, &nLen);
 45950  		/* Perform the lookup */
 45951  		if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
 45952  			SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
 45953  				/* Function is callable */
 45954  				res = 1;
 45955  		}
 45956  	}
 45957  	return res;
 45958  }
 45959  /*
 45960   * bool is_callable(callable $name[, bool $syntax_only = false])
 45961   * Verify that the contents of a variable can be called as a function.
 45962   * Parameters
 45963   * $name
 45964   *    The callback function to check
 45965   * $syntax_only
 45966   *    If set to TRUE the function only verifies that name might be a function or method.
 45967   *    It will only reject simple variables that are not strings, or an array that does
 45968   *    not have a valid structure to be used as a callback. The valid ones are supposed
 45969   *    to have only 2 entries, the first of which is an object or a string, and the second
 45970   *    a string.
 45971   * Return
 45972   *  TRUE if name is callable, FALSE otherwise.
 45973   */
 45974  static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg)
 45975  {
 45976  	jx9_vm *pVm;	
 45977  	int res;
 45978  	if( nArg < 1 ){
 45979  		/* Missing arguments, return FALSE */
 45980  		jx9_result_bool(pCtx, 0);
 45981  		return SXRET_OK;
 45982  	}
 45983  	/* Point to the target VM */
 45984  	pVm = pCtx->pVm;
 45985  	/* Perform the requested operation */
 45986  	res = jx9VmIsCallable(pVm, apArg[0]);
 45987  	jx9_result_bool(pCtx, res);
 45988  	return SXRET_OK;
 45989  }
 45990  /*
 45991   * Hash walker callback used by the [get_defined_functions()] function
 45992   * defined below.
 45993   */
 45994  static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData)
 45995  {
 45996  	jx9_value *pArray = (jx9_value *)pUserData;
 45997  	jx9_value sName;
 45998  	sxi32 rc;
 45999  	/* Prepare the function name for insertion */
 46000  	jx9MemObjInitFromString(pArray->pVm, &sName, 0);
 46001  	jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
 46002  	/* Perform the insertion */
 46003  	rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
 46004  	jx9MemObjRelease(&sName);
 46005  	return rc;
 46006  }
 46007  /*
 46008   * array get_defined_functions(void)
 46009   *  Returns an array of all defined functions.
 46010   * Parameter
 46011   *  None.
 46012   * Return
 46013   *  Returns an multidimensional array containing a list of all defined functions
 46014   *  both built-in (internal) and user-defined.
 46015   *  The internal functions will be accessible via $arr["internal"], and the user 
 46016   *  defined ones using $arr["user"]. 
 46017   * Note:
 46018   *  NULL is returned on failure.
 46019   */
 46020  static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46021  {
 46022  	jx9_value *pArray;
 46023  	/* NOTE:
 46024  	 * Don't worry about freeing memory here, every allocated resource will be released
 46025  	 * automatically by the engine as soon we return from this foreign function.
 46026  	 */
 46027  	pArray = jx9_context_new_array(pCtx);
 46028   	if( pArray == 0 ){
 46029  		SXUNUSED(nArg); /* cc warning */
 46030  		SXUNUSED(apArg);
 46031  		/* Return NULL */
 46032  		jx9_result_null(pCtx);
 46033  		return SXRET_OK;
 46034  	}
 46035  	/* Fill with the appropriate information */
 46036  	SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
 46037  	/* Fill with the appropriate information */
 46038  	SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
 46039  	/* Return a copy of the array array */
 46040  	jx9_result_value(pCtx, pArray);
 46041  	return SXRET_OK;
 46042  }
 46043  /*
 46044   * Call a user defined or foreign function where the name of the function
 46045   * is stored in the pFunc parameter and the given arguments are stored
 46046   * in the apArg[] array.
 46047   * Return SXRET_OK if the function was successfuly called.Any other
 46048   * return value indicates failure.
 46049   */
 46050  JX9_PRIVATE sxi32 jx9VmCallUserFunction(
 46051  	jx9_vm *pVm,       /* Target VM */
 46052  	jx9_value *pFunc,  /* Callback name */
 46053  	int nArg,          /* Total number of given arguments */
 46054  	jx9_value **apArg, /* Callback arguments */
 46055  	jx9_value *pResult /* Store callback return value here. NULL otherwise */
 46056  	)
 46057  {
 46058  	jx9_value *aStack;
 46059  	VmInstr aInstr[2];
 46060  	int i;
 46061  	if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
 46062  		/* Don't bother processing, it's invalid anyway */
 46063  		if( pResult ){
 46064  			/* Assume a null return value */
 46065  			jx9MemObjRelease(pResult);
 46066  		}
 46067  		return SXERR_INVALID;
 46068  	}
 46069  	/* Create a new operand stack */
 46070  	aStack = VmNewOperandStack(&(*pVm), 1+nArg);
 46071  	if( aStack == 0 ){
 46072  		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, 
 46073  			"JX9 is running out of memory while invoking user callback");
 46074  		if( pResult ){
 46075  			/* Assume a null return value */
 46076  			jx9MemObjRelease(pResult);
 46077  		}
 46078  		return SXERR_MEM;
 46079  	}
 46080  	/* Fill the operand stack with the given arguments */
 46081  	for( i = 0 ; i < nArg ; i++ ){
 46082  		jx9MemObjLoad(apArg[i], &aStack[i]);
 46083  		aStack[i].nIdx = apArg[i]->nIdx;
 46084  	}
 46085  	/* Push the function name */
 46086  	jx9MemObjLoad(pFunc, &aStack[i]);
 46087  	aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
 46088  	/* Emit the CALL istruction */
 46089  	aInstr[0].iOp = JX9_OP_CALL;
 46090  	aInstr[0].iP1 = nArg; /* Total number of given arguments */
 46091  	aInstr[0].iP2 = 0;
 46092  	aInstr[0].p3  = 0;
 46093  	/* Emit the DONE instruction */
 46094  	aInstr[1].iOp = JX9_OP_DONE;
 46095  	aInstr[1].iP1 = 1;   /* Extract function return value if available */
 46096  	aInstr[1].iP2 = 0;
 46097  	aInstr[1].p3  = 0;
 46098  	/* Execute the function body (if available) */
 46099  	VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult);
 46100  	/* Clean up the mess left behind */
 46101  	SyMemBackendFree(&pVm->sAllocator, aStack);
 46102  	return JX9_OK;
 46103  }
 46104  /*
 46105   * Call a user defined or foreign function whith a varibale number
 46106   * of arguments where the name of the function is stored in the pFunc
 46107   * parameter.
 46108   * Return SXRET_OK if the function was successfuly called.Any other
 46109   * return value indicates failure.
 46110   */
 46111  JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
 46112  	jx9_vm *pVm,       /* Target VM */
 46113  	jx9_value *pFunc,  /* Callback name */
 46114  	jx9_value *pResult, /* Store callback return value here. NULL otherwise */
 46115  	...                /* 0 (Zero) or more Callback arguments */ 
 46116  	)
 46117  {
 46118  	jx9_value *pArg;
 46119  	SySet aArg;
 46120  	va_list ap;
 46121  	sxi32 rc;
 46122  	SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
 46123  	/* Copy arguments one after one */
 46124  	va_start(ap, pResult);
 46125  	for(;;){
 46126  		pArg = va_arg(ap, jx9_value *);
 46127  		if( pArg == 0 ){
 46128  			break;
 46129  		}
 46130  		SySetPut(&aArg, (const void *)&pArg);
 46131  	}
 46132  	/* Call the core routine */
 46133  	rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
 46134  	/* Cleanup */
 46135  	SySetRelease(&aArg);
 46136  	return rc;
 46137  }
 46138  /*
 46139   * bool defined(string $name)
 46140   *  Checks whether a given named constant exists.
 46141   * Parameter:
 46142   *  Name of the desired constant.
 46143   * Return
 46144   *  TRUE if the given constant exists.FALSE otherwise.
 46145   */
 46146  static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46147  {
 46148  	const char *zName;
 46149  	int nLen = 0;
 46150  	int res = 0;
 46151  	if( nArg < 1 ){
 46152  		/* Missing constant name, return FALSE */
 46153  		jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
 46154  		jx9_result_bool(pCtx, 0);
 46155  		return SXRET_OK;
 46156  	}
 46157  	/* Extract constant name */
 46158  	zName = jx9_value_to_string(apArg[0], &nLen);
 46159  	/* Perform the lookup */
 46160  	if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){
 46161  		/* Already defined */
 46162  		res = 1;
 46163  	}
 46164  	jx9_result_bool(pCtx, res);
 46165  	return SXRET_OK;
 46166  }
 46167  /*
 46168   * Hash walker callback used by the [get_defined_constants()] function
 46169   * defined below.
 46170   */
 46171  static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
 46172  {
 46173  	jx9_value *pArray = (jx9_value *)pUserData;
 46174  	jx9_value sName;
 46175  	sxi32 rc;
 46176  	/* Prepare the constant name for insertion */
 46177  	jx9MemObjInitFromString(pArray->pVm, &sName, 0);
 46178  	jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
 46179  	/* Perform the insertion */
 46180  	rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
 46181  	jx9MemObjRelease(&sName);
 46182  	return rc;
 46183  }
 46184  /*
 46185   * array get_defined_constants(void)
 46186   *  Returns an associative array with the names of all defined
 46187   *  constants.
 46188   * Parameters
 46189   *  NONE.
 46190   * Returns
 46191   *  Returns the names of all the constants currently defined.
 46192   */
 46193  static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46194  {
 46195  	jx9_value *pArray;
 46196  	/* Create the array first*/
 46197  	pArray = jx9_context_new_array(pCtx);
 46198  	if( pArray == 0 ){
 46199  		SXUNUSED(nArg); /* cc warning */
 46200  		SXUNUSED(apArg);
 46201  		/* Return NULL */
 46202  		jx9_result_null(pCtx);
 46203  		return SXRET_OK;
 46204  	}
 46205  	/* Fill the array with the defined constants */
 46206  	SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
 46207  	/* Return the created array */
 46208  	jx9_result_value(pCtx, pArray);
 46209  	return SXRET_OK;
 46210  }
 46211  /*
 46212   * Section:
 46213   *  Random numbers/string generators.
 46214   * Authors:
 46215   *    Symisc Systems, devel@symisc.net.
 46216   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 46217   * Status:
 46218   *    Stable.
 46219   */
 46220  /*
 46221   * Generate a random 32-bit unsigned integer.
 46222   * JX9 use it's own private PRNG which is based on the one
 46223   * used by te SQLite3 library.
 46224   */
 46225  JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm)
 46226  {
 46227  	sxu32 iNum;
 46228  	SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32));
 46229  	return iNum;
 46230  }
 46231  /*
 46232   * Generate a random string (English Alphabet) of length nLen.
 46233   * Note that the generated string is NOT null terminated.
 46234   * JX9 use it's own private PRNG which is based on the one used
 46235   * by te SQLite3 library.
 46236   */
 46237  JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen)
 46238  {
 46239  	static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
 46240  	int i;
 46241  	/* Generate a binary string first */
 46242  	SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen);
 46243  	/* Turn the binary string into english based alphabet */
 46244  	for( i = 0 ; i < nLen ; ++i ){
 46245  		 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
 46246  	 }
 46247  }
 46248  /*
 46249   * int rand()
 46250   *  Generate a random (unsigned 32-bit) integer.
 46251   * Parameter
 46252   *  $min
 46253   *    The lowest value to return (default: 0)
 46254   *  $max
 46255   *   The highest value to return (default: getrandmax())
 46256   * Return
 46257   *   A pseudo random value between min (or 0) and max (or getrandmax(), inclusive).
 46258   * Note:
 46259   *  JX9 use it's own private PRNG which is based on the one used
 46260   *  by te SQLite3 library.
 46261   */
 46262  static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46263  {
 46264  	sxu32 iNum;
 46265  	/* Generate the random number */
 46266  	iNum = jx9VmRandomNum(pCtx->pVm);
 46267  	if( nArg > 1 ){
 46268  		sxu32 iMin, iMax;
 46269  		iMin = (sxu32)jx9_value_to_int(apArg[0]);
 46270  		iMax = (sxu32)jx9_value_to_int(apArg[1]);
 46271  		if( iMin < iMax ){
 46272  			sxu32 iDiv = iMax+1-iMin;
 46273  			if( iDiv > 0 ){
 46274  				iNum = (iNum % iDiv)+iMin;
 46275  			}
 46276  		}else if(iMax > 0 ){
 46277  			iNum %= iMax;
 46278  		}
 46279  	}
 46280  	/* Return the number */
 46281  	jx9_result_int64(pCtx, (jx9_int64)iNum);
 46282  	return SXRET_OK;
 46283  }
 46284  /*
 46285   * int getrandmax(void)
 46286   *   Show largest possible random value
 46287   * Return
 46288   *  The largest possible random value returned by rand() which is in
 46289   *  this implementation 0xFFFFFFFF.
 46290   * Note:
 46291   *  JX9 use it's own private PRNG which is based on the one used
 46292   *  by te SQLite3 library.
 46293   */
 46294  static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46295  {
 46296  	SXUNUSED(nArg); /* cc warning */
 46297  	SXUNUSED(apArg);
 46298  	jx9_result_int64(pCtx, SXU32_HIGH);
 46299  	return SXRET_OK;
 46300  }
 46301  /*
 46302   * string rand_str()
 46303   * string rand_str(int $len)
 46304   *  Generate a random string (English alphabet).
 46305   * Parameter
 46306   *  $len
 46307   *    Length of the desired string (default: 16, Min: 1, Max: 1024)
 46308   * Return
 46309   *   A pseudo random string.
 46310   * Note:
 46311   *  JX9 use it's own private PRNG which is based on the one used
 46312   *  by te SQLite3 library.
 46313   */
 46314  static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46315  {
 46316  	char zString[1024];
 46317  	int iLen = 0x10;
 46318  	if( nArg > 0 ){
 46319  		/* Get the desired length */
 46320  		iLen = jx9_value_to_int(apArg[0]);
 46321  		if( iLen < 1 || iLen > 1024 ){
 46322  			/* Default length */
 46323  			iLen = 0x10;
 46324  		}
 46325  	}
 46326  	/* Generate the random string */
 46327  	jx9VmRandomString(pCtx->pVm, zString, iLen);
 46328  	/* Return the generated string */
 46329  	jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
 46330  	return SXRET_OK;
 46331  }
 46332  /*
 46333   * Section:
 46334   *  Language construct implementation as foreign functions.
 46335   * Authors:
 46336   *    Symisc Systems, devel@symisc.net.
 46337   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 46338   * Status:
 46339   *    Stable.
 46340   */
 46341  /*
 46342   * void print($string...)
 46343   *  Output one or more messages.
 46344   * Parameters
 46345   *  $string
 46346   *   Message to output.
 46347   * Return
 46348   *  NULL.
 46349   */
 46350  static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
 46351  {
 46352  	const char *zData;
 46353  	int nDataLen = 0;
 46354  	jx9_vm *pVm;
 46355  	int i, rc;
 46356  	/* Point to the target VM */
 46357  	pVm = pCtx->pVm;
 46358  	/* Output */
 46359  	for( i = 0 ; i < nArg ; ++i ){
 46360  		zData = jx9_value_to_string(apArg[i], &nDataLen);
 46361  		if( nDataLen > 0 ){
 46362  			rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
 46363  			/* Increment output length */
 46364  			pVm->nOutputLen += nDataLen;
 46365  			if( rc == SXERR_ABORT ){
 46366  				/* Output consumer callback request an operation abort */
 46367  				return JX9_ABORT;
 46368  			}
 46369  		}
 46370  	}
 46371  	return SXRET_OK;
 46372  }
 46373  /*
 46374   * void exit(string $msg)
 46375   * void exit(int $status)
 46376   * void die(string $ms)
 46377   * void die(int $status)
 46378   *   Output a message and terminate program execution.
 46379   * Parameter
 46380   *  If status is a string, this function prints the status just before exiting.
 46381   *  If status is an integer, that value will be used as the exit status 
 46382   *  and not printed
 46383   * Return
 46384   *  NULL
 46385   */
 46386  static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46387  {
 46388  	if( nArg > 0 ){
 46389  		if( jx9_value_is_string(apArg[0]) ){
 46390  			const char *zData;
 46391  			int iLen = 0;
 46392  			/* Print exit message */
 46393  			zData = jx9_value_to_string(apArg[0], &iLen);
 46394  			jx9_context_output(pCtx, zData, iLen);
 46395  		}else if(jx9_value_is_int(apArg[0]) ){
 46396  			sxi32 iExitStatus;
 46397  			/* Record exit status code */
 46398  			iExitStatus = jx9_value_to_int(apArg[0]);
 46399  			pCtx->pVm->iExitStatus = iExitStatus;
 46400  		}
 46401  	}
 46402  	/* Abort processing immediately */
 46403  	return JX9_ABORT;
 46404  }
 46405  /*
 46406   * Unset a memory object [i.e: a jx9_value].
 46407   */
 46408  JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
 46409  {
 46410  	jx9_value *pObj;
 46411  	pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
 46412  	if( pObj ){
 46413  		VmSlot sFree;
 46414  		/* Release the object */
 46415  		jx9MemObjRelease(pObj);
 46416  		/* Restore to the free list */
 46417  		sFree.nIdx = nObjIdx;
 46418  		sFree.pUserData = 0;
 46419  		SySetPut(&pVm->aFreeObj, (const void *)&sFree);
 46420  	}				
 46421  	return SXRET_OK;
 46422  }
 46423  /*
 46424   * string gettype($var)
 46425   *  Get the type of a variable
 46426   * Parameters
 46427   *   $var
 46428   *    The variable being type checked.
 46429   * Return
 46430   *   String representation of the given variable type.
 46431   */
 46432  static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46433  {
 46434  	const char *zType = "null";
 46435  	if( nArg > 0 ){
 46436  		zType = jx9MemObjTypeDump(apArg[0]);
 46437  	}
 46438  	/* Return the variable type */
 46439  	jx9_result_string(pCtx, zType, -1/*Compute length automatically*/);
 46440  	return SXRET_OK;
 46441  }
 46442  /*
 46443   * string get_resource_type(resource $handle)
 46444   *  This function gets the type of the given resource.
 46445   * Parameters
 46446   *  $handle
 46447   *  The evaluated resource handle.
 46448   * Return
 46449   *  If the given handle is a resource, this function will return a string 
 46450   *  representing its type. If the type is not identified by this function
 46451   *  the return value will be the string Unknown.
 46452   *  This function will return FALSE and generate an error if handle
 46453   *  is not a resource.
 46454   */
 46455  static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46456  {
 46457  	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
 46458  		/* Missing/Invalid arguments, return FALSE*/
 46459  		jx9_result_bool(pCtx, 0);
 46460  		return SXRET_OK;
 46461  	}
 46462  	jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
 46463  	return SXRET_OK;
 46464  }
 46465  /*
 46466   * void dump(expression, ....)
 46467   *   dump — Dumps information about a variable
 46468   * Parameters
 46469   *   One or more expression to dump.
 46470   * Returns
 46471   *  Nothing.
 46472   */
 46473  static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46474  {
 46475  	SyBlob sDump; /* Generated dump is stored here */
 46476  	int i;
 46477  	SyBlobInit(&sDump,&pCtx->pVm->sAllocator);
 46478  	/* Dump one or more expressions */
 46479  	for( i = 0 ; i < nArg ; i++ ){
 46480  		jx9_value *pObj = apArg[i];
 46481  		/* Reset the working buffer */
 46482  		SyBlobReset(&sDump);
 46483  		/* Dump the given expression */
 46484  		jx9MemObjDump(&sDump,pObj);
 46485  		/* Output */
 46486  		if( SyBlobLength(&sDump) > 0 ){
 46487  			jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
 46488  		}
 46489  	}
 46490  	/* Release the working buffer */
 46491  	SyBlobRelease(&sDump);
 46492  	return SXRET_OK;
 46493  }
 46494  /*
 46495   * Section:
 46496   *  Version, Credits and Copyright related functions.
 46497   * Authors:
 46498   *    Symisc Systems, devel@symisc.net.
 46499   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 46500   *    Stable.
 46501   */
 46502  /*
 46503   * string jx9_version(void)
 46504   * string jx9_credits(void)
 46505   *  Returns the running version of the jx9 version.
 46506   * Parameters
 46507   *  None
 46508   * Return
 46509   * Current jx9 version.
 46510   */
 46511  static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46512  {
 46513  	SXUNUSED(nArg);
 46514  	SXUNUSED(apArg); /* cc warning */
 46515  	/* Current engine version, signature and cipyright notice */
 46516  	jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
 46517  	return JX9_OK;
 46518  }
 46519  /*
 46520   * Section:
 46521   *    URL related routines.
 46522   * Authors:
 46523   *    Symisc Systems, devel@symisc.net.
 46524   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 46525   * Status:
 46526   *    Stable.
 46527   */
 46528  /* Forward declaration */
 46529  static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen);
 46530  /*
 46531   * value parse_url(string $url [, int $component = -1 ])
 46532   *  Parse a URL and return its fields.
 46533   * Parameters
 46534   *  $url
 46535   *   The URL to parse.
 46536   * $component
 46537   *  Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
 46538   *  JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
 46539   *  just a specific URL component as a string (except when JX9_URL_PORT is given
 46540   *  in which case the return value will be an integer).
 46541   * Return
 46542   *  If the component parameter is omitted, an associative array is returned.
 46543   *  At least one element will be present within the array. Potential keys within
 46544   *  this array are:
 46545   *   scheme - e.g. http
 46546   *   host
 46547   *   port
 46548   *   user
 46549   *   pass
 46550   *   path
 46551   *   query - after the question mark ?
 46552   *   fragment - after the hashmark #
 46553   * Note:
 46554   *  FALSE is returned on failure.
 46555   *  This function work with relative URL unlike the one shipped
 46556   *  with the standard JX9 engine.
 46557   */
 46558  static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46559  {
 46560  	const char *zStr; /* Input string */
 46561  	SyString *pComp;  /* Pointer to the URI component */
 46562  	SyhttpUri sURI;   /* Parse of the given URI */
 46563  	int nLen;
 46564  	sxi32 rc;
 46565  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 46566  		/* Missing/Invalid arguments, return FALSE */
 46567  		jx9_result_bool(pCtx, 0);
 46568  		return JX9_OK;
 46569  	}
 46570  	/* Extract the given URI */
 46571  	zStr = jx9_value_to_string(apArg[0], &nLen);
 46572  	if( nLen < 1 ){
 46573  		/* Nothing to process, return FALSE */
 46574  		jx9_result_bool(pCtx, 0);
 46575  		return JX9_OK;
 46576  	}
 46577  	/* Get a parse */
 46578  	rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
 46579  	if( rc != SXRET_OK ){
 46580  		/* Malformed input, return FALSE */
 46581  		jx9_result_bool(pCtx, 0);
 46582  		return JX9_OK;
 46583  	}
 46584  	if( nArg > 1 ){
 46585  		int nComponent = jx9_value_to_int(apArg[1]);
 46586  		/* Refer to constant.c for constants values */
 46587  		switch(nComponent){
 46588  		case 1: /* JX9_URL_SCHEME */
 46589  			pComp = &sURI.sScheme;
 46590  			if( pComp->nByte < 1 ){
 46591  				/* No available value, return NULL */
 46592  				jx9_result_null(pCtx);
 46593  			}else{
 46594  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46595  			}
 46596  			break;
 46597  		case 2: /* JX9_URL_HOST */
 46598  			pComp = &sURI.sHost;
 46599  			if( pComp->nByte < 1 ){
 46600  				/* No available value, return NULL */
 46601  				jx9_result_null(pCtx);
 46602  			}else{
 46603  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46604  			}
 46605  			break;
 46606  		case 3: /* JX9_URL_PORT */
 46607  			pComp = &sURI.sPort;
 46608  			if( pComp->nByte < 1 ){
 46609  				/* No available value, return NULL */
 46610  				jx9_result_null(pCtx);
 46611  			}else{
 46612  				int iPort = 0;
 46613  				/* Cast the value to integer */
 46614  				SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
 46615  				jx9_result_int(pCtx, iPort);
 46616  			}
 46617  			break;
 46618  		case 4: /* JX9_URL_USER */
 46619  			pComp = &sURI.sUser;
 46620  			if( pComp->nByte < 1 ){
 46621  				/* No available value, return NULL */
 46622  				jx9_result_null(pCtx);
 46623  			}else{
 46624  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46625  			}
 46626  			break;
 46627  		case 5: /* JX9_URL_PASS */
 46628  			pComp = &sURI.sPass;
 46629  			if( pComp->nByte < 1 ){
 46630  				/* No available value, return NULL */
 46631  				jx9_result_null(pCtx);
 46632  			}else{
 46633  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46634  			}
 46635  			break;
 46636  		case 7: /* JX9_URL_QUERY */
 46637  			pComp = &sURI.sQuery;
 46638  			if( pComp->nByte < 1 ){
 46639  				/* No available value, return NULL */
 46640  				jx9_result_null(pCtx);
 46641  			}else{
 46642  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46643  			}
 46644  			break;
 46645  		case 8: /* JX9_URL_FRAGMENT */
 46646  			pComp = &sURI.sFragment;
 46647  			if( pComp->nByte < 1 ){
 46648  				/* No available value, return NULL */
 46649  				jx9_result_null(pCtx);
 46650  			}else{
 46651  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46652  			}
 46653  			break;
 46654  		case 6: /*  JX9_URL_PATH */
 46655  			pComp = &sURI.sPath;
 46656  			if( pComp->nByte < 1 ){
 46657  				/* No available value, return NULL */
 46658  				jx9_result_null(pCtx);
 46659  			}else{
 46660  				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
 46661  			}
 46662  			break;
 46663  		default:
 46664  			/* No such entry, return NULL */
 46665  			jx9_result_null(pCtx);
 46666  			break;
 46667  		}
 46668  	}else{
 46669  		jx9_value *pArray, *pValue;
 46670  		/* Return an associative array */
 46671  		pArray = jx9_context_new_array(pCtx);  /* Empty array */
 46672  		pValue = jx9_context_new_scalar(pCtx); /* Array value */
 46673  		if( pArray == 0 || pValue == 0 ){
 46674  			/* Out of memory */
 46675  			jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
 46676  			/* Return false */
 46677  			jx9_result_bool(pCtx, 0);
 46678  			return JX9_OK;
 46679  		}
 46680  		/* Fill the array */
 46681  		pComp = &sURI.sScheme;
 46682  		if( pComp->nByte > 0 ){
 46683  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46684  			jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
 46685  		}
 46686  		/* Reset the string cursor */
 46687  		jx9_value_reset_string_cursor(pValue);
 46688  		pComp = &sURI.sHost;
 46689  		if( pComp->nByte > 0 ){
 46690  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46691  			jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
 46692  		}
 46693  		/* Reset the string cursor */
 46694  		jx9_value_reset_string_cursor(pValue);
 46695  		pComp = &sURI.sPort;
 46696  		if( pComp->nByte > 0 ){
 46697  			int iPort = 0;/* cc warning */
 46698  			/* Convert to integer */
 46699  			SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
 46700  			jx9_value_int(pValue, iPort);
 46701  			jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
 46702  		}
 46703  		/* Reset the string cursor */
 46704  		jx9_value_reset_string_cursor(pValue);
 46705  		pComp = &sURI.sUser;
 46706  		if( pComp->nByte > 0 ){
 46707  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46708  			jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
 46709  		}
 46710  		/* Reset the string cursor */
 46711  		jx9_value_reset_string_cursor(pValue);
 46712  		pComp = &sURI.sPass;
 46713  		if( pComp->nByte > 0 ){
 46714  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46715  			jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
 46716  		}
 46717  		/* Reset the string cursor */
 46718  		jx9_value_reset_string_cursor(pValue);
 46719  		pComp = &sURI.sPath;
 46720  		if( pComp->nByte > 0 ){
 46721  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46722  			jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
 46723  		}
 46724  		/* Reset the string cursor */
 46725  		jx9_value_reset_string_cursor(pValue);
 46726  		pComp = &sURI.sQuery;
 46727  		if( pComp->nByte > 0 ){
 46728  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46729  			jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
 46730  		}
 46731  		/* Reset the string cursor */
 46732  		jx9_value_reset_string_cursor(pValue);
 46733  		pComp = &sURI.sFragment;
 46734  		if( pComp->nByte > 0 ){
 46735  			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
 46736  			jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
 46737  		}
 46738  		/* Return the created array */
 46739  		jx9_result_value(pCtx, pArray);
 46740  		/* NOTE:
 46741  		 * Don't worry about freeing 'pValue', everything will be released
 46742  		 * automatically as soon we return from this function.
 46743  		 */
 46744  	}
 46745  	/* All done */
 46746  	return JX9_OK;
 46747  }
 46748  /*
 46749   * Section:
 46750   *   Array related routines.
 46751   * Authors:
 46752   *    Symisc Systems, devel@symisc.net.
 46753   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 46754   * Status:
 46755   *    Stable.
 46756   * Note 2012-5-21 01:04:15:
 46757   *  Array related functions that need access to the underlying
 46758   *  virtual machine are implemented here rather than 'hashmap.c'
 46759   */
 46760  /*
 46761   * The [extract()] function store it's state information in an instance
 46762   * of the following structure.
 46763   */
 46764  typedef struct extract_aux_data extract_aux_data;
 46765  struct extract_aux_data
 46766  {
 46767  	jx9_vm *pVm;          /* VM that own this instance */
 46768  	int iCount;           /* Number of variables successfully imported  */
 46769  	const char *zPrefix;  /* Prefix name */
 46770  	int Prefixlen;        /* Prefix  length */
 46771  	int iFlags;           /* Control flags */
 46772  	char zWorker[1024];   /* Working buffer */
 46773  };
 46774  /* Forward declaration */
 46775  static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData);
 46776  /*
 46777   * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]])
 46778   *   Import variables into the current symbol table from an array.
 46779   * Parameters
 46780   * $var_array
 46781   *  An associative array. This function treats keys as variable names and values
 46782   *  as variable values. For each key/value pair it will create a variable in the current symbol
 46783   *  table, subject to extract_type and prefix parameters.
 46784   *  You must use an associative array; a numerically indexed array will not produce results
 46785   *  unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID.
 46786   * $extract_type
 46787   *  The way invalid/numeric keys and collisions are treated is determined by the extract_type.
 46788   *  It can be one of the following values:
 46789   *   EXTR_OVERWRITE
 46790   *       If there is a collision, overwrite the existing variable. 
 46791   *   EXTR_SKIP
 46792   *       If there is a collision, don't overwrite the existing variable. 
 46793   *   EXTR_PREFIX_SAME
 46794   *       If there is a collision, prefix the variable name with prefix. 
 46795   *   EXTR_PREFIX_ALL
 46796   *       Prefix all variable names with prefix. 
 46797   *   EXTR_PREFIX_INVALID
 46798   *       Only prefix invalid/numeric variable names with prefix. 
 46799   *   EXTR_IF_EXISTS
 46800   *       Only overwrite the variable if it already exists in the current symbol table
 46801   *       otherwise do nothing.
 46802   *       This is useful for defining a list of valid variables and then extracting only those
 46803   *       variables you have defined out of $_REQUEST, for example. 
 46804   *   EXTR_PREFIX_IF_EXISTS
 46805   *       Only create prefixed variable names if the non-prefixed version of the same variable exists in 
 46806   *      the current symbol table.
 46807   * $prefix
 46808   *  Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL
 46809   *  EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name
 46810   *  it is not imported into the symbol table. Prefixes are automatically separated from the array key by an
 46811   *  underscore character.
 46812   * Return
 46813   *   Returns the number of variables successfully imported into the symbol table.
 46814   */
 46815  static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg)
 46816  {
 46817  	extract_aux_data sAux;
 46818  	jx9_hashmap *pMap;
 46819  	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
 46820  		/* Missing/Invalid arguments, return 0 */
 46821  		jx9_result_int(pCtx, 0);
 46822  		return JX9_OK;
 46823  	}
 46824  	/* Point to the target hashmap */
 46825  	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
 46826  	if( pMap->nEntry < 1 ){
 46827  		/* Empty map, return  0 */
 46828  		jx9_result_int(pCtx, 0);
 46829  		return JX9_OK;
 46830  	}
 46831  	/* Prepare the aux data */
 46832  	SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
 46833  	if( nArg > 1 ){
 46834  		sAux.iFlags = jx9_value_to_int(apArg[1]);
 46835  		if( nArg > 2 ){
 46836  			sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
 46837  		}
 46838  	}
 46839  	sAux.pVm = pCtx->pVm;
 46840  	/* Invoke the worker callback */
 46841  	jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
 46842  	/* Number of variables successfully imported */
 46843  	jx9_result_int(pCtx, sAux.iCount);
 46844  	return JX9_OK;
 46845  }
 46846  /*
 46847   * Worker callback for the [extract()] function defined
 46848   * below.
 46849   */
 46850  static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
 46851  {
 46852  	extract_aux_data *pAux = (extract_aux_data *)pUserData;
 46853  	int iFlags = pAux->iFlags;
 46854  	jx9_vm *pVm = pAux->pVm;
 46855  	jx9_value *pObj;
 46856  	SyString sVar;
 46857  	if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){
 46858  		iFlags |= 0x08; /*EXTR_PREFIX_ALL*/
 46859  	}
 46860  	/* Perform a string cast */
 46861  	jx9MemObjToString(pKey);
 46862  	if( SyBlobLength(&pKey->sBlob) < 1 ){
 46863  		/* Unavailable variable name */
 46864  		return SXRET_OK;
 46865  	}
 46866  	sVar.nByte = 0; /* cc warning */
 46867  	if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){
 46868  		sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s", 
 46869  			pAux->Prefixlen, pAux->zPrefix, 
 46870  			SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
 46871  			);
 46872  	}else{
 46873  		sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker, 
 46874  			SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker)));
 46875  	}
 46876  	sVar.zString = pAux->zWorker;
 46877  	/* Try to extract the variable */
 46878  	pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE);
 46879  	if( pObj ){
 46880  		/* Collision */
 46881  		if( iFlags & 0x02 /* EXTR_SKIP */ ){
 46882  			return SXRET_OK;
 46883  		}
 46884  		if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){
 46885  			if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){
 46886  				/* Already prefixed */
 46887  				return SXRET_OK;
 46888  			}
 46889  			sVar.nByte = SyBufferFormat(
 46890  				pAux->zWorker, sizeof(pAux->zWorker),
 46891  				"%.*s_%.*s", 
 46892  				pAux->Prefixlen, pAux->zPrefix, 
 46893  				SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
 46894  				);
 46895  			pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
 46896  		}
 46897  	}else{
 46898  		/* Create the variable */
 46899  		pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
 46900  	}
 46901  	if( pObj ){
 46902  		/* Overwrite the old value */
 46903  		jx9MemObjStore(pValue, pObj);
 46904  		/* Increment counter */
 46905  		pAux->iCount++;
 46906  	}
 46907  	return SXRET_OK;
 46908  }
 46909  /*
 46910   * Compile and evaluate a JX9 chunk at run-time.
 46911   * Refer to the include language construct implementation for more
 46912   * information.
 46913   */
 46914  static sxi32 VmEvalChunk(
 46915  	jx9_vm *pVm,        /* Underlying Virtual Machine */
 46916  	jx9_context *pCtx,  /* Call Context */
 46917  	SyString *pChunk,   /* JX9 chunk to evaluate */ 
 46918  	int iFlags,         /* Compile flag */
 46919  	int bTrueReturn     /* TRUE to return execution result */
 46920  	)
 46921  {
 46922  	SySet *pByteCode, aByteCode;
 46923  	ProcConsumer xErr = 0;
 46924  	void *pErrData = 0;
 46925  	/* Initialize bytecode container */
 46926  	SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr));
 46927  	SySetAlloc(&aByteCode, 0x20);
 46928  	/* Reset the code generator */
 46929  	if( bTrueReturn ){
 46930  		/* Included file, log compile-time errors */
 46931  		xErr = pVm->pEngine->xConf.xErr;
 46932  		pErrData = pVm->pEngine->xConf.pErrData;
 46933  	}
 46934  	jx9ResetCodeGenerator(pVm, xErr, pErrData);
 46935  	/* Swap bytecode container */
 46936  	pByteCode = pVm->pByteContainer;
 46937  	pVm->pByteContainer = &aByteCode;
 46938  	/* Compile the chunk */
 46939  	jx9CompileScript(pVm, pChunk, iFlags);
 46940  	if( pVm->sCodeGen.nErr > 0 ){
 46941  		/* Compilation error, return false */
 46942  		if( pCtx ){
 46943  			jx9_result_bool(pCtx, 0);
 46944  		}
 46945  	}else{
 46946  		jx9_value sResult; /* Return value */
 46947  		if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
 46948  			/* Out of memory */
 46949  			if( pCtx ){
 46950  				jx9_result_bool(pCtx, 0);
 46951  			}
 46952  			goto Cleanup;
 46953  		}
 46954  		if( bTrueReturn ){
 46955  			/* Assume a boolean true return value */
 46956  			jx9MemObjInitFromBool(pVm, &sResult, 1);
 46957  		}else{
 46958  			/* Assume a null return value */
 46959  			jx9MemObjInit(pVm, &sResult);
 46960  		}
 46961  		/* Execute the compiled chunk */
 46962  		VmLocalExec(pVm, &aByteCode, &sResult);
 46963  		if( pCtx ){
 46964  			/* Set the execution result */
 46965  			jx9_result_value(pCtx, &sResult);
 46966  		}
 46967  		jx9MemObjRelease(&sResult);
 46968  	}
 46969  Cleanup:
 46970  	/* Cleanup the mess left behind */
 46971  	pVm->pByteContainer = pByteCode;
 46972  	SySetRelease(&aByteCode);
 46973  	return SXRET_OK;
 46974  }
 46975  /*
 46976   * Check if a file path is already included.
 46977   */
 46978  static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile)
 46979  {
 46980  	SyString *aEntries;
 46981  	sxu32 n;
 46982  	aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded);
 46983  	/* Perform a linear search */
 46984  	for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){
 46985  		if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){
 46986  			/* Already included */
 46987  			return TRUE;
 46988  		}
 46989  	}
 46990  	return FALSE;
 46991  }
 46992  /*
 46993   * Push a file path in the appropriate VM container.
 46994   */
 46995  JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew)
 46996  {
 46997  	SyString sPath;
 46998  	char *zDup;
 46999  #ifdef __WINNT__
 47000  	char *zCur;
 47001  #endif
 47002  	sxi32 rc;
 47003  	if( nLen < 0 ){
 47004  		nLen = SyStrlen(zPath);
 47005  	}
 47006  	/* Duplicate the file path first */
 47007  	zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen);
 47008  	if( zDup == 0 ){
 47009  		return SXERR_MEM;
 47010  	}
 47011  #ifdef __WINNT__
 47012  	/* Normalize path on windows
 47013  	 * Example:
 47014  	 *    Path/To/File.jx9
 47015  	 * becomes
 47016  	 *   path\to\file.jx9
 47017  	 */
 47018  	zCur = zDup;
 47019  	while( zCur[0] != 0 ){
 47020  		if( zCur[0] == '/' ){
 47021  			zCur[0] = '\\';
 47022  		}else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){
 47023  			int c = SyToLower(zCur[0]);
 47024  			zCur[0] = (char)c; /* MSVC stupidity */
 47025  		}
 47026  		zCur++;
 47027  	}
 47028  #endif
 47029  	/* Install the file path */
 47030  	SyStringInitFromBuf(&sPath, zDup, nLen);
 47031  	if( !bMain ){
 47032  		if( VmIsIncludedFile(&(*pVm), &sPath) ){
 47033  			/* Already included */
 47034  			*pNew = 0;
 47035  		}else{
 47036  			/* Insert in the corresponding container */
 47037  			rc = SySetPut(&pVm->aIncluded, (const void *)&sPath);
 47038  			if( rc != SXRET_OK ){
 47039  				SyMemBackendFree(&pVm->sAllocator, zDup);
 47040  				return rc;
 47041  			}
 47042  			*pNew = 1;
 47043  		}
 47044  	}
 47045  	SySetPut(&pVm->aFiles, (const void *)&sPath);
 47046  	return SXRET_OK;
 47047  }
 47048  /*
 47049   * Compile and Execute a JX9 script at run-time.
 47050   * SXRET_OK is returned on sucessful evaluation.Any other return values
 47051   * indicates failure.
 47052   * Note that the JX9 script to evaluate can be a local or remote file.In
 47053   * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
 47054   * operations.
 47055   * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
 47056   * this function is a no-op.
 47057   * Refer to the implementation of the include(), import() language
 47058   * constructs for more information.
 47059   */
 47060  static sxi32 VmExecIncludedFile(
 47061  	 jx9_context *pCtx, /* Call Context */
 47062  	 SyString *pPath,   /* Script path or URL*/
 47063  	 int IncludeOnce    /* TRUE if called from import() or require_once() */
 47064  	 )
 47065  {
 47066  	sxi32 rc;
 47067  #ifndef JX9_DISABLE_BUILTIN_FUNC
 47068  	const jx9_io_stream *pStream;
 47069  	SyBlob sContents;
 47070  	void *pHandle;
 47071  	jx9_vm *pVm;
 47072  	int isNew;
 47073  	/* Initialize fields */
 47074  	pVm = pCtx->pVm;
 47075  	SyBlobInit(&sContents, &pVm->sAllocator);
 47076  	isNew = 0;
 47077  	/* Extract the associated stream */
 47078  	pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
 47079  	/*
 47080  	 * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"] 
 47081  	 * in a read-only mode.
 47082  	 */
 47083  	pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew);
 47084  	if( pHandle == 0 ){
 47085  		return SXERR_IO;
 47086  	}
 47087  	rc = SXRET_OK; /* Stupid cc warning */
 47088  	if( IncludeOnce && !isNew ){
 47089  		/* Already included */
 47090  		rc = SXERR_EXISTS;
 47091  	}else{
 47092  		/* Read the whole file contents */
 47093  		rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents);
 47094  		if( rc == SXRET_OK ){
 47095  			SyString sScript;
 47096  			/* Compile and execute the script */
 47097  			SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
 47098  			VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE);
 47099  		}
 47100  	}
 47101  	/* Pop from the set of included file */
 47102  	(void)SySetPop(&pVm->aFiles);
 47103  	/* Close the handle */
 47104  	jx9StreamCloseHandle(pStream, pHandle);
 47105  	/* Release the working buffer */
 47106  	SyBlobRelease(&sContents);
 47107  #else
 47108  	pCtx = 0; /* cc warning */
 47109  	pPath = 0;
 47110  	IncludeOnce = 0;
 47111  	rc = SXERR_IO;
 47112  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 47113  	return rc;
 47114  }
 47115  /* * include:
 47116   * According to the JX9 reference manual.
 47117   *  The include() function includes and evaluates the specified file.
 47118   *  Files are included based on the file path given or, if none is given
 47119   *  the include_path specified.If the file isn't found in the include_path
 47120   *  include() will finally check in the calling script's own directory
 47121   *  and the current working directory before failing. The include()
 47122   *  construct will emit a warning if it cannot find a file; this is different
 47123   *  behavior from require(), which will emit a fatal error.
 47124   *  If a path is defined — whether absolute (starting with a drive letter
 47125   *  or \ on Windows, or / on Unix/Linux systems) or relative to the current
 47126   *  directory (starting with . or ..) — the include_path will be ignored altogether.
 47127   *  For example, if a filename begins with ../, the parser will look in the parent
 47128   *  directory to find the requested file.
 47129   *  When a file is included, the code it contains inherits the variable scope
 47130   *  of the line on which the include occurs. Any variables available at that line
 47131   *  in the calling file will be available within the called file, from that point forward.
 47132   *  However, all functions and objectes defined in the included file have the global scope. 
 47133   */
 47134  static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47135  {
 47136  	SyString sFile;
 47137  	sxi32 rc;
 47138  	if( nArg < 1 ){
 47139  		/* Nothing to evaluate, return NULL */
 47140  		jx9_result_null(pCtx);
 47141  		return SXRET_OK;
 47142  	}
 47143  	/* File to include */
 47144  	sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
 47145  	if( sFile.nByte < 1 ){
 47146  		/* Empty string, return NULL */
 47147  		jx9_result_null(pCtx);
 47148  		return SXRET_OK;
 47149  	}
 47150  	/* Open, compile and execute the desired script */
 47151  	rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE);
 47152  	if( rc != SXRET_OK ){
 47153  		/* Emit a warning and return false */
 47154  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
 47155  		jx9_result_bool(pCtx, 0);
 47156  	}
 47157  	return SXRET_OK;
 47158  }
 47159  /*
 47160   * import:
 47161   *  According to the JX9 reference manual.
 47162   *   The import() statement includes and evaluates the specified file during
 47163   *   the execution of the script. This is a behavior similar to the include() 
 47164   *   statement, with the only difference being that if the code from a file has already
 47165   *   been included, it will not be included again. As the name suggests, it will be included
 47166   *   just once.
 47167   */
 47168  static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47169  {
 47170  	SyString sFile;
 47171  	sxi32 rc;
 47172  	if( nArg < 1 ){
 47173  		/* Nothing to evaluate, return NULL */
 47174  		jx9_result_null(pCtx);
 47175  		return SXRET_OK;
 47176  	}
 47177  	/* File to include */
 47178  	sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
 47179  	if( sFile.nByte < 1 ){
 47180  		/* Empty string, return NULL */
 47181  		jx9_result_null(pCtx);
 47182  		return SXRET_OK;
 47183  	}
 47184  	/* Open, compile and execute the desired script */
 47185  	rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
 47186  	if( rc == SXERR_EXISTS ){
 47187  		/* File already included, return TRUE */
 47188  		jx9_result_bool(pCtx, 1);
 47189  		return SXRET_OK;
 47190  	}
 47191  	if( rc != SXRET_OK ){
 47192  		/* Emit a warning and return false */
 47193  		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
 47194  		jx9_result_bool(pCtx, 0);
 47195   	}
 47196  	return SXRET_OK;
 47197  }
 47198  /*
 47199   * Section:
 47200   *  Command line arguments processing.
 47201   * Authors:
 47202   *    Symisc Systems, devel@symisc.net.
 47203   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 47204   * Status:
 47205   *    Stable.
 47206   */
 47207  /*
 47208   * Check if a short option argument [i.e: -c] is available in the command
 47209   * line string. Return a pointer to the start of the stream on success.
 47210   * NULL otherwise.
 47211   */
 47212  static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd)
 47213  {
 47214  	while( zIn < zEnd ){
 47215  		if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){
 47216  			/* Got one */
 47217  			return &zIn[1];
 47218  		}
 47219  		/* Advance the cursor */
 47220  		zIn++;
 47221  	}
 47222  	/* No such option */
 47223  	return 0;
 47224  }
 47225  /*
 47226   * Check if a long option argument [i.e: --opt] is available in the command
 47227   * line string. Return a pointer to the start of the stream on success.
 47228   * NULL otherwise.
 47229   */
 47230  static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd)
 47231  {
 47232  	const char *zOpt;
 47233  	while( zIn < zEnd ){
 47234  		if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){
 47235  			zIn += 2;
 47236  			zOpt = zIn;
 47237  			while( zIn < zEnd && !SyisSpace(zIn[0]) ){
 47238  				if( zIn[0] == '=' /* --opt=val */){
 47239  					break;
 47240  				}
 47241  				zIn++;
 47242  			}
 47243  			/* Test */
 47244  			if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){
 47245  				/* Got one, return it's value */
 47246  				return zIn;
 47247  			}
 47248  
 47249  		}else{
 47250  			zIn++;
 47251  		}
 47252  	}
 47253  	/* No such option */
 47254  	return 0;
 47255  }
 47256  /*
 47257   * Long option [i.e: --opt] arguments private data structure.
 47258   */
 47259  struct getopt_long_opt
 47260  {
 47261  	const char *zArgIn, *zArgEnd; /* Command line arguments */
 47262  	jx9_value *pWorker;  /* Worker variable*/
 47263  	jx9_value *pArray;   /* getopt() return value */
 47264  	jx9_context *pCtx;   /* Call Context */
 47265  };
 47266  /* Forward declaration */
 47267  static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
 47268  /*
 47269   * Extract short or long argument option values.
 47270   */
 47271  static void VmExtractOptArgValue(
 47272  	jx9_value *pArray,  /* getopt() return value */
 47273  	jx9_value *pWorker, /* Worker variable */
 47274  	const char *zArg,   /* Argument stream */
 47275  	const char *zArgEnd, /* End of the argument stream  */
 47276  	int need_val,       /* TRUE to fetch option argument */
 47277  	jx9_context *pCtx,  /* Call Context */
 47278  	const char *zName   /* Option name */)
 47279  {
 47280  	jx9_value_bool(pWorker, 0);
 47281  	if( !need_val ){
 47282  		/* 
 47283  		 * Option does not need arguments.
 47284  		 * Insert the option name and a boolean FALSE.
 47285  		 */
 47286  		jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
 47287  	}else{
 47288  		const char *zCur;
 47289  		/* Extract option argument */
 47290  		zArg++;
 47291  		if( zArg < zArgEnd && zArg[0] == '=' ){
 47292  			zArg++;
 47293  		}
 47294  		while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
 47295  			zArg++;
 47296  		}
 47297  		if( zArg >= zArgEnd || zArg[0] == '-' ){
 47298  			/*
 47299  			 * Argument not found.
 47300  			 * Insert the option name and a boolean FALSE.
 47301  			 */
 47302  			jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
 47303  			return;
 47304  		}
 47305  		/* Delimit the value */
 47306  		zCur = zArg;
 47307  		if( zArg[0] == '\'' || zArg[0] == '"' ){
 47308  			int d = zArg[0];
 47309  			/* Delimt the argument */
 47310  			zArg++;
 47311  			zCur = zArg;
 47312  			while( zArg < zArgEnd ){
 47313  				if( zArg[0] == d && zArg[-1] != '\\' ){
 47314  					/* Delimiter found, exit the loop  */
 47315  					break;
 47316  				}
 47317  				zArg++;
 47318  			}
 47319  			/* Save the value */
 47320  			jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
 47321  			if( zArg < zArgEnd ){ zArg++; }
 47322  		}else{
 47323  			while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
 47324  				zArg++;
 47325  			}
 47326  			/* Save the value */
 47327  			jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
 47328  		}
 47329  		/*
 47330  		 * Check if we are dealing with multiple values.
 47331  		 * If so, create an array to hold them, rather than a scalar variable.
 47332  		 */
 47333  		while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
 47334  			zArg++;
 47335  		}
 47336  		if( zArg < zArgEnd && zArg[0] != '-' ){
 47337  			jx9_value *pOptArg; /* Array of option arguments */
 47338  			pOptArg = jx9_context_new_array(pCtx);
 47339  			if( pOptArg == 0 ){
 47340  				jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
 47341  			}else{
 47342  				/* Insert the first value */
 47343  				jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
 47344  				for(;;){
 47345  					if( zArg >= zArgEnd || zArg[0] == '-' ){
 47346  						/* No more value */
 47347  						break;
 47348  					}
 47349  					/* Delimit the value */
 47350  					zCur = zArg;
 47351  					if( zArg < zArgEnd && zArg[0] == '\\' ){
 47352  						zArg++;
 47353  						zCur = zArg;
 47354  					}
 47355  					while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
 47356  						zArg++;
 47357  					}
 47358  					/* Reset the string cursor */
 47359  					jx9_value_reset_string_cursor(pWorker);
 47360  					/* Save the value */
 47361  					jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
 47362  					/* Insert */
 47363  					jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
 47364  					/* Jump trailing white spaces */
 47365  					while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
 47366  						zArg++;
 47367  					}
 47368  				}
 47369  				/* Insert the option arg array */
 47370  				jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
 47371  				/* Safely release */
 47372  				jx9_context_release_value(pCtx, pOptArg);
 47373  			}
 47374  		}else{
 47375  			/* Single value */
 47376  			jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
 47377  		}
 47378  	}
 47379  }
 47380  /*
 47381   * array getopt(string $options[, array $longopts ])
 47382   *   Gets options from the command line argument list.
 47383   * Parameters
 47384   *  $options
 47385   *   Each character in this string will be used as option characters
 47386   *   and matched against options passed to the script starting with
 47387   *   a single hyphen (-). For example, an option string "x" recognizes
 47388   *   an option -x. Only a-z, A-Z and 0-9 are allowed.
 47389   *  $longopts
 47390   *   An array of options. Each element in this array will be used as option
 47391   *   strings and matched against options passed to the script starting with
 47392   *   two hyphens (--). For example, an longopts element "opt" recognizes an
 47393   *   option --opt. 
 47394   * Return
 47395   *  This function will return an array of option / argument pairs or FALSE
 47396   *  on failure. 
 47397   */
 47398  static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47399  {
 47400  	const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
 47401  	struct getopt_long_opt sLong;
 47402  	jx9_value *pArray, *pWorker;
 47403  	SyBlob *pArg;
 47404  	int nByte;
 47405  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 47406  		/* Missing/Invalid arguments, return FALSE */
 47407  		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
 47408  		jx9_result_bool(pCtx, 0);
 47409  		return JX9_OK;
 47410  	}
 47411  	/* Extract option arguments */
 47412  	zIn  = jx9_value_to_string(apArg[0], &nByte);
 47413  	zEnd = &zIn[nByte];
 47414  	/* Point to the string representation of the $argv[] array */
 47415  	pArg = &pCtx->pVm->sArgv;
 47416  	/* Create a new empty array and a worker variable */
 47417  	pArray = jx9_context_new_array(pCtx);
 47418  	pWorker = jx9_context_new_scalar(pCtx);
 47419  	if( pArray == 0 || pWorker == 0 ){
 47420  		jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
 47421  		jx9_result_bool(pCtx, 0);
 47422  		return JX9_OK;
 47423  	}
 47424  	if( SyBlobLength(pArg) < 1 ){
 47425  		/* Empty command line, return the empty array*/
 47426  		jx9_result_value(pCtx, pArray);
 47427  		/* Everything will be released automatically when we return 
 47428  		 * from this function.
 47429  		 */
 47430  		return JX9_OK;
 47431  	}
 47432  	zArgIn = (const char *)SyBlobData(pArg);
 47433  	zArgEnd = &zArgIn[SyBlobLength(pArg)];
 47434  	/* Fill the long option structure */
 47435  	sLong.pArray = pArray;
 47436  	sLong.pWorker = pWorker;
 47437  	sLong.zArgIn =  zArgIn;
 47438  	sLong.zArgEnd = zArgEnd;
 47439  	sLong.pCtx = pCtx;
 47440  	/* Start processing */
 47441  	while( zIn < zEnd ){
 47442  		int c = zIn[0];
 47443  		int need_val = 0;
 47444  		/* Advance the stream cursor */
 47445  		zIn++;
 47446  		/* Ignore non-alphanum characters */
 47447  		if( !SyisAlphaNum(c) ){
 47448  			continue;
 47449  		}
 47450  		if( zIn < zEnd && zIn[0] == ':' ){
 47451  			zIn++;
 47452  			need_val = 1;
 47453  			if( zIn < zEnd && zIn[0] == ':' ){
 47454  				zIn++;
 47455  			}
 47456  		}
 47457  		/* Find option */
 47458  		zArg = VmFindShortOpt(c, zArgIn, zArgEnd);
 47459  		if( zArg == 0 ){
 47460  			/* No such option */
 47461  			continue;
 47462  		}
 47463  		/* Extract option argument value */
 47464  		VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c);	
 47465  	}
 47466  	if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
 47467  		/* Process long options */
 47468  		jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
 47469  	}
 47470  	/* Return the option array */
 47471  	jx9_result_value(pCtx, pArray);
 47472  	/* 
 47473  	 * Don't worry about freeing memory, everything will be released
 47474  	 * automatically as soon we return from this foreign function.
 47475  	 */
 47476  	return JX9_OK;
 47477  }
 47478  /*
 47479   * Array walker callback used for processing long options values.
 47480   */
 47481  static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData)
 47482  {
 47483  	struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData;
 47484  	const char *zArg, *zOpt, *zEnd;
 47485  	int need_value = 0;
 47486  	int nByte;
 47487  	/* Value must be of type string */
 47488  	if( !jx9_value_is_string(pValue) ){
 47489  		/* Simply ignore */
 47490  		return JX9_OK;
 47491  	}
 47492  	zOpt = jx9_value_to_string(pValue, &nByte);
 47493  	if( nByte < 1 ){
 47494  		/* Empty string, ignore */
 47495  		return JX9_OK;
 47496  	}
 47497  	zEnd = &zOpt[nByte - 1];
 47498  	if( zEnd[0] == ':' ){
 47499  		char *zTerm;
 47500  		/* Try to extract a value */
 47501  		need_value = 1;
 47502  		while( zEnd >= zOpt && zEnd[0] == ':' ){
 47503  			zEnd--;
 47504  		}
 47505  		if( zOpt >= zEnd ){
 47506  			/* Empty string, ignore */
 47507  			SXUNUSED(pKey);
 47508  			return JX9_OK;
 47509  		}
 47510  		zEnd++;
 47511  		zTerm = (char *)zEnd;
 47512  		zTerm[0] = 0;
 47513  	}else{
 47514  		zEnd = &zOpt[nByte];
 47515  	}
 47516  	/* Find the option */
 47517  	zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd);
 47518  	if( zArg == 0 ){
 47519  		/* No such option, return immediately */
 47520  		return JX9_OK;
 47521  	}
 47522  	/* Try to extract a value */
 47523  	VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
 47524  	return JX9_OK;
 47525  }
 47526  /*
 47527   * int utf8_encode(string $input)
 47528   *  UTF-8 encoding.
 47529   *  This function encodes the string data to UTF-8, and returns the encoded version.
 47530   *  UTF-8 is a standard mechanism used by Unicode for encoding wide character values
 47531   * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized
 47532   * (meaning it is possible for a program to figure out where in the bytestream characters start)
 47533   * and can be used with normal string comparison functions for sorting and such.
 47534   *  Notes on UTF-8 (According to SQLite3 authors):
 47535   *  Byte-0    Byte-1    Byte-2    Byte-3    Value
 47536   *  0xxxxxxx                                 00000000 00000000 0xxxxxxx
 47537   *  110yyyyy  10xxxxxx                       00000000 00000yyy yyxxxxxx
 47538   *  1110zzzz  10yyyyyy  10xxxxxx             00000000 zzzzyyyy yyxxxxxx
 47539   *  11110uuu  10uuzzzz  10yyyyyy  10xxxxxx   000uuuuu zzzzyyyy yyxxxxxx
 47540   * Parameters
 47541   * $input
 47542   *   String to encode or NULL on failure.
 47543   * Return
 47544   *  An UTF-8 encoded string.
 47545   */
 47546  static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47547  {
 47548  	const unsigned char *zIn, *zEnd;
 47549  	int nByte, c, e;
 47550  	if( nArg < 1 ){
 47551  		/* Missing arguments, return null */
 47552  		jx9_result_null(pCtx);
 47553  		return JX9_OK;
 47554  	}
 47555  	/* Extract the target string */
 47556  	zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
 47557  	if( nByte < 1 ){
 47558  		/* Empty string, return null */
 47559  		jx9_result_null(pCtx);
 47560  		return JX9_OK;
 47561  	}
 47562  	zEnd = &zIn[nByte];
 47563  	/* Start the encoding process */
 47564  	for(;;){
 47565  		if( zIn >= zEnd ){
 47566  			/* End of input */
 47567  			break;
 47568  		}
 47569  		c = zIn[0];
 47570  		/* Advance the stream cursor */
 47571  		zIn++;
 47572  		/* Encode */
 47573  		if( c<0x00080 ){
 47574  			e = (c&0xFF);
 47575  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47576  		}else if( c<0x00800 ){
 47577  			e = 0xC0 + ((c>>6)&0x1F);
 47578  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47579  			e = 0x80 + (c & 0x3F);
 47580  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47581  		}else if( c<0x10000 ){
 47582  			e = 0xE0 + ((c>>12)&0x0F);
 47583  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47584  			e = 0x80 + ((c>>6) & 0x3F);
 47585  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47586  			e = 0x80 + (c & 0x3F);
 47587  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47588  		}else{
 47589  			e = 0xF0 + ((c>>18) & 0x07);
 47590  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47591  			e = 0x80 + ((c>>12) & 0x3F);
 47592  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47593  			e = 0x80 + ((c>>6) & 0x3F);
 47594  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47595  			e = 0x80 + (c & 0x3F);
 47596  			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
 47597  		} 
 47598  	}
 47599  	/* All done */
 47600  	return JX9_OK;
 47601  }
 47602  /*
 47603   * UTF-8 decoding routine extracted from the sqlite3 source tree.
 47604   * Original author: D. Richard Hipp (http://www.sqlite.org)
 47605   * Status: Public Domain
 47606   */
 47607  /*
 47608  ** This lookup table is used to help decode the first byte of
 47609  ** a multi-byte UTF8 character.
 47610  */
 47611  static const unsigned char UtfTrans1[] = {
 47612    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 47613    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
 47614    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
 47615    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
 47616    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 47617    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
 47618    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
 47619    0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, 
 47620  };
 47621  /*
 47622  ** Translate a single UTF-8 character.  Return the unicode value.
 47623  **
 47624  ** During translation, assume that the byte that zTerm points
 47625  ** is a 0x00.
 47626  **
 47627  ** Write a pointer to the next unread byte back into *pzNext.
 47628  **
 47629  ** Notes On Invalid UTF-8:
 47630  **
 47631  **  *  This routine never allows a 7-bit character (0x00 through 0x7f) to
 47632  **     be encoded as a multi-byte character.  Any multi-byte character that
 47633  **     attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
 47634  **
 47635  **  *  This routine never allows a UTF16 surrogate value to be encoded.
 47636  **     If a multi-byte character attempts to encode a value between
 47637  **     0xd800 and 0xe000 then it is rendered as 0xfffd.
 47638  **
 47639  **  *  Bytes in the range of 0x80 through 0xbf which occur as the first
 47640  **     byte of a character are interpreted as single-byte characters
 47641  **     and rendered as themselves even though they are technically
 47642  **     invalid characters.
 47643  **
 47644  **  *  This routine accepts an infinite number of different UTF8 encodings
 47645  **     for unicode values 0x80 and greater.  It do not change over-length
 47646  **     encodings to 0xfffd as some systems recommend.
 47647  */
 47648  #define READ_UTF8(zIn, zTerm, c)                           \
 47649    c = *(zIn++);                                            \
 47650    if( c>=0xc0 ){                                           \
 47651      c = UtfTrans1[c-0xc0];                                 \
 47652      while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){            \
 47653        c = (c<<6) + (0x3f & *(zIn++));                      \
 47654      }                                                      \
 47655      if( c<0x80                                             \
 47656          || (c&0xFFFFF800)==0xD800                          \
 47657          || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }        \
 47658    }
 47659  JX9_PRIVATE int jx9Utf8Read(
 47660    const unsigned char *z,         /* First byte of UTF-8 character */
 47661    const unsigned char *zTerm,     /* Pretend this byte is 0x00 */
 47662    const unsigned char **pzNext    /* Write first byte past UTF-8 char here */
 47663  ){
 47664    int c;
 47665    READ_UTF8(z, zTerm, c);
 47666    *pzNext = z;
 47667    return c;
 47668  }
 47669  /*
 47670   * string utf8_decode(string $data)
 47671   *  This function decodes data, assumed to be UTF-8 encoded, to unicode.
 47672   * Parameters
 47673   * data
 47674   *  An UTF-8 encoded string.
 47675   * Return
 47676   *  Unicode decoded string or NULL on failure.
 47677   */
 47678  static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47679  {
 47680  	const unsigned char *zIn, *zEnd;
 47681  	int nByte, c;
 47682  	if( nArg < 1 ){
 47683  		/* Missing arguments, return null */
 47684  		jx9_result_null(pCtx);
 47685  		return JX9_OK;
 47686  	}
 47687  	/* Extract the target string */
 47688  	zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
 47689  	if( nByte < 1 ){
 47690  		/* Empty string, return null */
 47691  		jx9_result_null(pCtx);
 47692  		return JX9_OK;
 47693  	}
 47694  	zEnd = &zIn[nByte];
 47695  	/* Start the decoding process */
 47696  	while( zIn < zEnd ){
 47697  		c = jx9Utf8Read(zIn, zEnd, &zIn);
 47698  		if( c == 0x0 ){
 47699  			break;
 47700  		}
 47701  		jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
 47702  	}
 47703  	return JX9_OK;
 47704  }
 47705  /*
 47706   * string json_encode(mixed $value)
 47707   *  Returns a string containing the JSON representation of value.
 47708   * Parameters
 47709   *  $value
 47710   *  The value being encoded. Can be any type except a resource.
 47711   * Return
 47712   *  Returns a JSON encoded string on success. FALSE otherwise
 47713   */
 47714  static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
 47715  {
 47716  	SyBlob sBlob;
 47717  	if( nArg < 1 ){
 47718  		/* Missing arguments, return FALSE */
 47719  		jx9_result_bool(pCtx, 0);
 47720  		return JX9_OK;
 47721  	}
 47722  	/* Init the working buffer */
 47723  	SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
 47724  	/* Perform the encoding operation */
 47725  	jx9JsonSerialize(apArg[0],&sBlob);
 47726  	/* Return the serialized value */
 47727  	jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
 47728  	/* Cleanup */
 47729  	SyBlobRelease(&sBlob);
 47730  	/* All done */
 47731  	return JX9_OK;
 47732  }
 47733  /*
 47734   * mixed json_decode(string $json)
 47735   *  Takes a JSON encoded string and converts it into a JX9 variable.
 47736   * Parameters
 47737   *  $json
 47738   *    The json string being decoded.
 47739   * Return
 47740   *  The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive)
 47741   *  are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
 47742   *  or if the encoded data is deeper than the recursion limit.
 47743   */
 47744  static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
 47745  {
 47746  	const char *zJSON;
 47747  	int nByte;
 47748  	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
 47749  		/* Missing/Invalid arguments, return NULL */
 47750  		jx9_result_null(pCtx);
 47751  		return JX9_OK;
 47752  	}
 47753  	/* Extract the JSON string */
 47754  	zJSON = jx9_value_to_string(apArg[0], &nByte);
 47755  	if( nByte < 1 ){
 47756  		/* Empty string, return NULL */
 47757  		jx9_result_null(pCtx);
 47758  		return JX9_OK;
 47759  	}
 47760  	/* Decode the raw JSON */
 47761  	jx9JsonDecode(pCtx,zJSON,nByte);
 47762  	return JX9_OK;
 47763  }
 47764  /* Table of built-in VM functions. */
 47765  static const jx9_builtin_func aVmFunc[] = {
 47766  	     /* JSON Encoding/Decoding */
 47767  	{ "json_encode",     vm_builtin_json_encode   },
 47768  	{ "json_decode",     vm_builtin_json_decode   },
 47769  	     /* Functions calls */
 47770  	{ "func_num_args"  , vm_builtin_func_num_args }, 
 47771  	{ "func_get_arg"   , vm_builtin_func_get_arg  }, 
 47772  	{ "func_get_args"  , vm_builtin_func_get_args }, 
 47773  	{ "function_exists", vm_builtin_func_exists   }, 
 47774  	{ "is_callable"    , vm_builtin_is_callable   }, 
 47775  	{ "get_defined_functions", vm_builtin_get_defined_func },  
 47776  	    /* Constants management */
 47777  	{ "defined",  vm_builtin_defined              }, 
 47778  	{ "get_defined_constants", vm_builtin_get_defined_constants }, 
 47779  	   /* Random numbers/strings generators */
 47780  	{ "rand",          vm_builtin_rand            }, 
 47781  	{ "rand_str",      vm_builtin_rand_str        }, 
 47782  	{ "getrandmax",    vm_builtin_getrandmax      }, 
 47783  	   /* Language constructs functions */
 47784  	{ "print", vm_builtin_print                   }, 
 47785  	{ "exit",  vm_builtin_exit                    }, 
 47786  	{ "die",   vm_builtin_exit                    },  
 47787  	  /* Variable handling functions */ 
 47788  	{ "gettype",   vm_builtin_gettype              }, 
 47789  	{ "get_resource_type", vm_builtin_get_resource_type},
 47790  	 /* Variable dumping */
 47791  	{ "dump",     vm_builtin_dump                 },
 47792  	  /* Release info */
 47793  	{"jx9_version",       vm_builtin_jx9_version  }, 
 47794  	{"jx9_credits",       vm_builtin_jx9_version  }, 
 47795  	{"jx9_info",          vm_builtin_jx9_version  },
 47796  	{"jx9_copyright",     vm_builtin_jx9_version  }, 
 47797  	  /* hashmap */
 47798  	{"extract",          vm_builtin_extract       }, 
 47799  	  /* URL related function */
 47800  	{"parse_url",        vm_builtin_parse_url     }, 
 47801  	   /* UTF-8 encoding/decoding */
 47802  	{"utf8_encode",    vm_builtin_utf8_encode}, 
 47803  	{"utf8_decode",    vm_builtin_utf8_decode}, 
 47804  	   /* Command line processing */
 47805  	{"getopt",         vm_builtin_getopt     }, 
 47806  	   /* Files/URI inclusion facility */
 47807  	{ "include",      vm_builtin_include          }, 
 47808  	{ "import", vm_builtin_import     }
 47809  };
 47810  /*
 47811   * Register the built-in VM functions defined above.
 47812   */
 47813  static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm)
 47814  {
 47815  	sxi32 rc;
 47816  	sxu32 n;
 47817  	for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){
 47818  		/* Note that these special functions have access
 47819  		 * to the underlying virtual machine as their
 47820  		 * private data.
 47821  		 */
 47822  		rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
 47823  		if( rc != SXRET_OK ){
 47824  			return rc;
 47825  		}
 47826  	}
 47827  	return SXRET_OK;
 47828  }
 47829  #ifndef JX9_DISABLE_BUILTIN_FUNC
 47830  /*
 47831   * Extract the IO stream device associated with a given scheme.
 47832   * Return a pointer to an instance of jx9_io_stream when the scheme
 47833   * have an associated IO stream registered with it. NULL otherwise.
 47834   * If no scheme:// is avalilable then the file:// scheme is assumed.
 47835   * For more information on how to register IO stream devices, please
 47836   * refer to the official documentation.
 47837   */
 47838  JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
 47839  	jx9_vm *pVm,           /* Target VM */
 47840  	const char **pzDevice, /* Full path, URI, ... */
 47841  	int nByte              /* *pzDevice length*/
 47842  	)
 47843  {
 47844  	const char *zIn, *zEnd, *zCur, *zNext;
 47845  	jx9_io_stream **apStream, *pStream;
 47846  	SyString sDev, sCur;
 47847  	sxu32 n, nEntry;
 47848  	int rc;
 47849  	/* Check if a scheme [i.e: file://, http://, zip://...] is available */
 47850  	zNext = zCur = zIn = *pzDevice;
 47851  	zEnd = &zIn[nByte];
 47852  	while( zIn < zEnd ){
 47853  		if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){
 47854  			/* Got one */
 47855  			zNext = &zIn[sizeof("://")-1];
 47856  			break;
 47857  		}
 47858  		/* Advance the cursor */
 47859  		zIn++;
 47860  	}
 47861  	if( zIn >= zEnd ){
 47862  		/* No such scheme, return the default stream */
 47863  		return pVm->pDefStream;
 47864  	}
 47865  	SyStringInitFromBuf(&sDev, zCur, zIn-zCur);
 47866  	/* Remove leading and trailing white spaces */
 47867  	SyStringFullTrim(&sDev);
 47868  	/* Perform a linear lookup on the installed stream devices */
 47869  	apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream);
 47870  	nEntry = SySetUsed(&pVm->aIOstream);
 47871  	for( n = 0 ; n < nEntry ; n++ ){
 47872  		pStream = apStream[n];
 47873  		SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName));
 47874  		/* Perfrom a case-insensitive comparison */
 47875  		rc = SyStringCmp(&sDev, &sCur, SyStrnicmp);
 47876  		if( rc == 0 ){
 47877  			/* Stream device found */
 47878  			*pzDevice = zNext;
 47879  			return pStream;
 47880  		}
 47881  	}
 47882  	/* No such stream, return NULL */
 47883  	return 0;
 47884  }
 47885  #endif /* JX9_DISABLE_BUILTIN_FUNC */
 47886  /*
 47887   * Section:
 47888   *    HTTP/URI related routines.
 47889   * Authors:
 47890   *    Symisc Systems, devel@symisc.net.
 47891   *    Copyright (C) Symisc Systems, http://jx9.symisc.net
 47892   * Status:
 47893   *    Stable.
 47894   */ 
 47895   /*
 47896    * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...].
 47897    * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document]
 47898    * This almost, but not quite, RFC1738 URI syntax.
 47899    * This routine is not a validator, it does not check for validity
 47900    * nor decode URI parts, the only thing this routine does is splitting
 47901    * the input to its fields.
 47902    * Upper layer are responsible of decoding and validating URI parts.
 47903    * On success, this function populate the "SyhttpUri" structure passed
 47904    * as the first argument. Otherwise SXERR_* is returned when a malformed
 47905    * input is encountered.
 47906    */
 47907   static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen)
 47908   {
 47909  	 const char *zEnd = &zUri[nLen];
 47910  	 sxu8 bHostOnly = FALSE;
 47911  	 sxu8 bIPv6 = FALSE	; 
 47912  	 const char *zCur;
 47913  	 SyString *pComp;
 47914  	 sxu32 nPos = 0;
 47915  	 sxi32 rc;
 47916  	 /* Zero the structure first */
 47917  	 SyZero(pOut, sizeof(SyhttpUri));
 47918  	 /* Remove leading and trailing white spaces  */
 47919  	 SyStringInitFromBuf(&pOut->sRaw, zUri, nLen);
 47920  	 SyStringFullTrim(&pOut->sRaw);
 47921  	 /* Find the first '/' separator */
 47922  	 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
 47923  	 if( rc != SXRET_OK ){
 47924  		 /* Assume a host name only */
 47925  		 zCur = zEnd;
 47926  		 bHostOnly = TRUE;
 47927  		 goto ProcessHost;
 47928  	 }
 47929  	 zCur = &zUri[nPos];
 47930  	 if( zUri != zCur && zCur[-1] == ':' ){
 47931  		 /* Extract a scheme:
 47932  		  * Not that we can get an invalid scheme here.
 47933  		  * Fortunately the caller can discard any URI by comparing this scheme with its 
 47934  		  * registered schemes and will report the error as soon as his comparison function
 47935  		  * fail.
 47936  		  */
 47937  	 	pComp = &pOut->sScheme;
 47938  		SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1));
 47939  		SyStringLeftTrim(pComp);		
 47940  	 }
 47941  	 if( zCur[1] != '/' ){
 47942  		 if( zCur == zUri || zCur[-1] == ':' ){
 47943  		  /* No authority */
 47944  		  goto PathSplit;
 47945  		}
 47946  		 /* There is something here , we will assume its an authority
 47947  		  * and someone has forgot the two prefix slashes "//", 
 47948  		  * sooner or later we will detect if we are dealing with a malicious
 47949  		  * user or not, but now assume we are dealing with an authority
 47950  		  * and let the caller handle all the validation process.
 47951  		  */
 47952  		 goto ProcessHost;
 47953  	 }	 
 47954  	 zUri = &zCur[2];
 47955  	 zCur = zEnd;
 47956  	 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
 47957  	 if( rc == SXRET_OK ){
 47958  		 zCur = &zUri[nPos];
 47959  	 }
 47960   ProcessHost:
 47961  	 /* Extract user information if present */
 47962  	 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos);
 47963  	 if( rc == SXRET_OK ){
 47964  		 if( nPos > 0 ){
 47965  			 sxu32 nPassOfft; /* Password offset */
 47966  			 pComp = &pOut->sUser;
 47967  			 SyStringInitFromBuf(pComp, zUri, nPos);
 47968  			 /* Extract the password if available */
 47969  			 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft);
 47970  			 if( rc == SXRET_OK && nPassOfft < nPos){
 47971  				 pComp->nByte = nPassOfft;
 47972  				 pComp = &pOut->sPass;
 47973  				 pComp->zString = &zUri[nPassOfft+sizeof(char)];
 47974  				 pComp->nByte = nPos - nPassOfft - 1;
 47975  			 }
 47976  			 /* Update the cursor */
 47977  			 zUri = &zUri[nPos+1];
 47978  		 }else{
 47979  			 zUri++;
 47980  		 }
 47981  	 }
 47982  	 pComp = &pOut->sHost;
 47983  	 while( zUri < zCur && SyisSpace(zUri[0])){
 47984  		 zUri++;
 47985  	 }	
 47986  	 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri));
 47987  	 if( pComp->zString[0] == '[' ){
 47988  		 /* An IPv6 Address: Make a simple naive test
 47989  		  */
 47990  		 zUri++; pComp->zString++; pComp->nByte = 0;
 47991  		 while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){
 47992  			 zUri++; pComp->nByte++;
 47993  		 }
 47994  		 if( zUri[0] != ']' ){
 47995  			 return SXERR_CORRUPT; /* Malformed IPv6 address */
 47996  		 }
 47997  		 zUri++;
 47998  		 bIPv6 = TRUE;
 47999  	 }
 48000  	 /* Extract a port number if available */
 48001  	 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos);
 48002  	 if( rc == SXRET_OK ){
 48003  		 if( bIPv6 == FALSE ){
 48004  			 pComp->nByte = (sxu32)(&zUri[nPos] - zUri);
 48005  		 }
 48006  		 pComp = &pOut->sPort;
 48007  		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1]));	
 48008  	 }
 48009  	 if( bHostOnly == TRUE ){
 48010  		 return SXRET_OK;
 48011  	 }
 48012  PathSplit:
 48013  	 zUri = zCur;
 48014  	 pComp = &pOut->sPath;
 48015  	 SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri));
 48016  	 if( pComp->nByte == 0 ){
 48017  		 return SXRET_OK; /* Empty path */
 48018  	 }
 48019  	 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){
 48020  		 pComp->nByte = nPos; /* Update path length */
 48021  		 pComp = &pOut->sQuery;
 48022  		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]));
 48023  	 }
 48024  	 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){
 48025  		 /* Update path or query length */
 48026  		 if( pComp == &pOut->sPath ){
 48027  			 pComp->nByte = nPos;
 48028  		 }else{
 48029  			 if( &zUri[nPos] < (char *)SyStringData(pComp) ){
 48030  				 /* Malformed syntax : Query must be present before fragment */
 48031  				 return SXERR_SYNTAX;
 48032  			 }
 48033  			 pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]);
 48034  		 }
 48035  		 pComp = &pOut->sFragment;
 48036  		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]))
 48037  	 }
 48038  	 return SXRET_OK;
 48039   }
 48040   /*
 48041   * Extract a single line from a raw HTTP request.
 48042   * Return SXRET_OK on success, SXERR_EOF when end of input
 48043   * and SXERR_MORE when more input is needed.
 48044   */
 48045  static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent)
 48046  { 
 48047    	const char *zIn;
 48048    	sxu32 nPos; 
 48049  	/* Jump leading white spaces */
 48050  	SyStringLeftTrim(pCursor);
 48051  	if( pCursor->nByte < 1 ){
 48052  		SyStringInitFromBuf(pCurrent, 0, 0);
 48053  		return SXERR_EOF; /* End of input */
 48054  	}
 48055  	zIn = SyStringData(pCursor);
 48056  	if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){
 48057  		/* Line not found, tell the caller to read more input from source */
 48058  		SyStringDupPtr(pCurrent, pCursor);
 48059  		return SXERR_MORE;
 48060  	}
 48061    	pCurrent->zString = zIn;
 48062    	pCurrent->nByte	= nPos;	
 48063    	/* advance the cursor so we can call this routine again */
 48064    	pCursor->zString = &zIn[nPos];
 48065    	pCursor->nByte -= nPos;
 48066    	return SXRET_OK;
 48067   }
 48068   /*
 48069    * Split a single MIME header into a name value pair. 
 48070    * This function return SXRET_OK, SXERR_CONTINUE on success.
 48071    * Otherwise SXERR_NEXT is returned when a malformed header
 48072    * is encountered.
 48073    * Note: This function handle also mult-line headers.
 48074    */
 48075   static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen)
 48076   {
 48077  	 SyString *pName;
 48078  	 sxu32 nPos;
 48079  	 sxi32 rc;
 48080  	 if( nLen < 1 ){
 48081  		 return SXERR_NEXT;
 48082  	 }
 48083  	 /* Check for multi-line header */
 48084  	if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){
 48085  		SyString *pTmp = &pLast->sValue;
 48086  		SyStringFullTrim(pTmp);
 48087  		if( pTmp->nByte == 0 ){
 48088  			SyStringInitFromBuf(pTmp, zLine, nLen);
 48089  		}else{
 48090  			/* Update header value length */
 48091  			pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString);
 48092  		}
 48093  		 /* Simply tell the caller to reset its states and get another line */
 48094  		 return SXERR_CONTINUE;
 48095  	 }
 48096  	/* Split the header */
 48097  	pName = &pHdr->sName;
 48098  	rc = SyByteFind(zLine, nLen, ':', &nPos);
 48099  	if(rc != SXRET_OK ){
 48100  		return SXERR_NEXT; /* Malformed header;Check the next entry */
 48101  	}
 48102  	SyStringInitFromBuf(pName, zLine, nPos);
 48103  	SyStringFullTrim(pName);
 48104  	/* Extract a header value */
 48105  	SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1);
 48106  	/* Remove leading and trailing whitespaces */
 48107  	SyStringFullTrim(&pHdr->sValue);
 48108  	return SXRET_OK;
 48109   }
 48110   /*
 48111    * Extract all MIME headers associated with a HTTP request.
 48112    * After processing the first line of a HTTP request, the following
 48113    * routine is called in order to extract MIME headers.
 48114    * This function return SXRET_OK on success, SXERR_MORE when it needs
 48115    * more inputs.
 48116    * Note: Any malformed header is simply discarded.
 48117    */
 48118   static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut)
 48119   {
 48120  	 SyhttpHeader *pLast = 0;
 48121  	 SyString sCurrent;
 48122  	 SyhttpHeader sHdr;
 48123  	 sxu8 bEol;
 48124  	 sxi32 rc;
 48125  	 if( SySetUsed(pOut) > 0 ){
 48126  		 pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1);
 48127  	 }
 48128  	 bEol = FALSE;
 48129  	 for(;;){
 48130  		 SyZero(&sHdr, sizeof(SyhttpHeader));
 48131  		 /* Extract a single line from the raw HTTP request */
 48132  		 rc = VmGetNextLine(pRequest, &sCurrent);
 48133  		 if(rc != SXRET_OK ){
 48134  			 if( sCurrent.nByte < 1 ){
 48135  				 break;
 48136  			 }
 48137  			 bEol = TRUE;
 48138  		 }
 48139  		 /* Process the header */
 48140  		 if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){
 48141  			 if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){
 48142  				 break;
 48143  			 }
 48144  			 /* Retrieve the last parsed header so we can handle multi-line header
 48145  			  * in case we face one of them.
 48146  			  */
 48147  			 pLast = (SyhttpHeader *)SySetPeek(pOut);
 48148  		 }
 48149  		 if( bEol ){
 48150  			 break;
 48151  		 }
 48152  	 } /* for(;;) */
 48153  	 return SXRET_OK;
 48154   }
 48155   /*
 48156    * Process the first line of a HTTP request.
 48157    * This routine perform the following operations
 48158    *  1) Extract the HTTP method.
 48159    *  2) Split the request URI to it's fields [ie: host, path, query, ...].
 48160    *  3) Extract the HTTP protocol version.
 48161    */
 48162   static sxi32 VmHttpProcessFirstLine(
 48163  	 SyString *pRequest, /* Raw HTTP request */
 48164  	 sxi32 *pMethod,     /* OUT: HTTP method */
 48165  	 SyhttpUri *pUri,    /* OUT: Parse of the URI */
 48166  	 sxi32 *pProto       /* OUT: HTTP protocol */
 48167  	 )
 48168   {
 48169  	 static const char *azMethods[] = { "get", "post", "head", "put"};
 48170  	 static const sxi32 aMethods[]  = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT};
 48171  	 const char *zIn, *zEnd, *zPtr;
 48172  	 SyString sLine;
 48173  	 sxu32 nLen;
 48174  	 sxi32 rc;
 48175  	 /* Extract the first line and update the pointer */
 48176  	 rc = VmGetNextLine(pRequest, &sLine);
 48177  	 if( rc != SXRET_OK ){
 48178  		 return rc;
 48179  	 }
 48180  	 if ( sLine.nByte < 1 ){
 48181  		 /* Empty HTTP request */
 48182  		 return SXERR_EMPTY;
 48183  	 }
 48184  	 /* Delimit the line and ignore trailing and leading white spaces */
 48185  	 zIn = sLine.zString;
 48186  	 zEnd = &zIn[sLine.nByte];
 48187  	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
 48188  		 zIn++;
 48189  	 }
 48190  	 /* Extract the HTTP method */
 48191  	 zPtr = zIn;
 48192  	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
 48193  		 zIn++;
 48194  	 }
 48195  	 *pMethod = HTTP_METHOD_OTHR;
 48196  	 if( zIn > zPtr ){
 48197  		 sxu32 i;
 48198  		 nLen = (sxu32)(zIn-zPtr);
 48199  		 for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){
 48200  			 if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){
 48201  				 *pMethod = aMethods[i];
 48202  				 break;
 48203  			 }
 48204  		 }
 48205  	 }
 48206  	 /* Jump trailing white spaces */
 48207  	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
 48208  		 zIn++;
 48209  	 }
 48210  	  /* Extract the request URI */
 48211  	 zPtr = zIn;
 48212  	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
 48213  		 zIn++;
 48214  	 } 
 48215  	 if( zIn > zPtr ){
 48216  		 nLen = (sxu32)(zIn-zPtr);
 48217  		 /* Split raw URI to it's fields */
 48218  		 VmHttpSplitURI(pUri, zPtr, nLen);
 48219  	 }
 48220  	 /* Jump trailing white spaces */
 48221  	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
 48222  		 zIn++;
 48223  	 }
 48224  	 /* Extract the HTTP version */
 48225  	 zPtr = zIn;
 48226  	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
 48227  		 zIn++;
 48228  	 }
 48229  	 *pProto = HTTP_PROTO_11; /* HTTP/1.1 */
 48230  	 rc = 1;
 48231  	 if( zIn > zPtr ){
 48232  		 rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr));
 48233  	 }
 48234  	 if( !rc ){
 48235  		 *pProto = HTTP_PROTO_10; /* HTTP/1.0 */
 48236  	 }
 48237  	 return SXRET_OK;
 48238   }
 48239   /*
 48240    * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded" 
 48241    * into a name value pair.
 48242    * Note that this encoding is implicit in GET based requests.
 48243    * After the tokenization process, register the decoded queries
 48244    * in the $_GET/$_POST/$_REQUEST superglobals arrays.
 48245    */
 48246   static sxi32 VmHttpSplitEncodedQuery(
 48247  	 jx9_vm *pVm,       /* Target VM */
 48248  	 SyString *pQuery,  /* Raw query to decode */
 48249  	 SyBlob *pWorker,   /* Working buffer */
 48250  	 int is_post        /* TRUE if we are dealing with a POST request */
 48251  	 )
 48252   {
 48253  	 const char *zEnd = &pQuery->zString[pQuery->nByte];
 48254  	 const char *zIn = pQuery->zString;
 48255  	 jx9_value *pGet, *pRequest;
 48256  	 SyString sName, sValue;
 48257  	 const char *zPtr;
 48258  	 sxu32 nBlobOfft;
 48259  	 /* Extract superglobals */
 48260  	 if( is_post ){
 48261  		 /* $_POST superglobal */
 48262  		 pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1);
 48263  	 }else{
 48264  		 /* $_GET superglobal */
 48265  		 pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1);
 48266  	 }
 48267  	 pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1);
 48268  	 /* Split up the raw query */
 48269  	 for(;;){
 48270  		 /* Jump leading white spaces */
 48271  		 while(zIn < zEnd  && SyisSpace(zIn[0]) ){
 48272  			 zIn++;
 48273  		 }
 48274  		 if( zIn >= zEnd ){
 48275  			 break;
 48276  		 }
 48277  		 zPtr = zIn;
 48278  		 while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){
 48279  			 zPtr++;
 48280  		 }
 48281  		 /* Reset the working buffer */
 48282  		 SyBlobReset(pWorker);
 48283  		 /* Decode the entry */
 48284  		 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
 48285  		 /* Save the entry */
 48286  		 sName.nByte = SyBlobLength(pWorker);
 48287  		 sValue.zString = 0;
 48288  		 sValue.nByte = 0;
 48289  		 if( zPtr < zEnd && zPtr[0] == '=' ){
 48290  			 zPtr++;
 48291  			 zIn = zPtr;
 48292  			 /* Store field value */
 48293  			 while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){
 48294  				 zPtr++;
 48295  			 }
 48296  			 if( zPtr > zIn ){
 48297  				 /* Decode the value */
 48298  				  nBlobOfft = SyBlobLength(pWorker);
 48299  				  SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
 48300  				  sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft);
 48301  				  sValue.nByte = SyBlobLength(pWorker) - nBlobOfft;
 48302  				 
 48303  			 }
 48304  			 /* Synchronize pointers */
 48305  			 zIn = zPtr;
 48306  		 }
 48307  		 sName.zString = (const char *)SyBlobData(pWorker);
 48308  		 /* Install the decoded query in the $_GET/$_REQUEST array */
 48309  		 if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){
 48310  			 VmHashmapInsert((jx9_hashmap *)pGet->x.pOther, 
 48311  				 sName.zString, (int)sName.nByte, 
 48312  				 sValue.zString, (int)sValue.nByte
 48313  				 );
 48314  		 }
 48315  		 if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
 48316  			 VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther, 
 48317  				 sName.zString, (int)sName.nByte, 
 48318  				 sValue.zString, (int)sValue.nByte
 48319  					 );
 48320  		 }
 48321  		 /* Advance the pointer */
 48322  		 zIn = &zPtr[1];
 48323  	 }
 48324  	/* All done*/
 48325  	return SXRET_OK;
 48326   }
 48327   /*
 48328    * Extract MIME header value from the given set.
 48329    * Return header value on success. NULL otherwise.
 48330    */
 48331   static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte)
 48332   {
 48333  	 SyhttpHeader *aMime, *pMime;
 48334  	 SyString sMime;
 48335  	 sxu32 n;
 48336  	 SyStringInitFromBuf(&sMime, zMime, nByte);
 48337  	 /* Point to the MIME entries */
 48338  	 aMime = (SyhttpHeader *)SySetBasePtr(pSet);
 48339  	 /* Perform the lookup */
 48340  	 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
 48341  		 pMime = &aMime[n];
 48342  		 if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){
 48343  			 /* Header found, return it's associated value */
 48344  			 return &pMime->sValue;
 48345  		 }
 48346  	 }
 48347  	 /* No such MIME header */
 48348  	 return 0;
 48349   }
 48350   /*
 48351    * Tokenize and decode a raw "Cookie:" MIME header into a name value pair
 48352    * and insert it's fields [i.e name, value] in the $_COOKIE superglobal.
 48353    */
 48354   static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
 48355   {
 48356  	 const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
 48357  	 SyString sName, sValue;
 48358  	 jx9_value *pCookie;
 48359  	 sxu32 nOfft;
 48360  	 /* Make sure the $_COOKIE superglobal is available */
 48361  	 pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1);
 48362  	 if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){
 48363  		 /* $_COOKIE superglobal not available */
 48364  		 return SXERR_NOTFOUND;
 48365  	 }	
 48366  	 for(;;){
 48367  		  /* Jump leading white spaces */
 48368  		 while( zIn < zEnd && SyisSpace(zIn[0]) ){
 48369  			 zIn++;
 48370  		 }
 48371  		 if( zIn >= zEnd ){
 48372  			 break;
 48373  		 }
 48374  		  /* Reset the working buffer */
 48375  		 SyBlobReset(pWorker);
 48376  		 zDelimiter = zIn;
 48377  		 /* Delimit the name[=value]; pair */ 
 48378  		 while( zDelimiter < zEnd && zDelimiter[0] != ';' ){
 48379  			 zDelimiter++;
 48380  		 }
 48381  		 zPtr = zIn;
 48382  		 while( zPtr < zDelimiter && zPtr[0] != '=' ){
 48383  			 zPtr++;
 48384  		 }
 48385  		 /* Decode the cookie */
 48386  		 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
 48387  		 sName.nByte = SyBlobLength(pWorker);
 48388  		 zPtr++;
 48389  		 sValue.zString = 0;
 48390  		 sValue.nByte = 0;
 48391  		 if( zPtr < zDelimiter ){
 48392  			 /* Got a Cookie value */
 48393  			 nOfft = SyBlobLength(pWorker);
 48394  			 SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE);
 48395  			 SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft);
 48396  		 }
 48397  		 /* Synchronize pointers */
 48398  		 zIn = &zDelimiter[1];
 48399  		 /* Perform the insertion */
 48400  		 sName.zString = (const char *)SyBlobData(pWorker);
 48401  		 VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther, 
 48402  			 sName.zString, (int)sName.nByte, 
 48403  			 sValue.zString, (int)sValue.nByte
 48404  			 );
 48405  	 }
 48406  	 return SXRET_OK;
 48407   }
 48408   /*
 48409    * Process a full HTTP request and populate the appropriate arrays
 48410    * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information
 48411    * extracted from the raw HTTP request. As an extension Symisc introduced 
 48412    * the $_HEADER array which hold a copy of the processed HTTP MIME headers
 48413    * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...].
 48414    * This function return SXRET_OK on success. Any other return value indicates
 48415    * a malformed HTTP request.
 48416    */
 48417   static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte)
 48418   {
 48419  	 SyString *pName, *pValue, sRequest; /* Raw HTTP request */
 48420  	 jx9_value *pHeaderArray;          /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/
 48421  	 SyhttpHeader *pHeader;            /* MIME header */
 48422  	 SyhttpUri sUri;     /* Parse of the raw URI*/
 48423  	 SyBlob sWorker;     /* General purpose working buffer */
 48424  	 SySet sHeader;      /* MIME headers set */
 48425  	 sxi32 iMethod;      /* HTTP method [i.e: GET, POST, HEAD...]*/
 48426  	 sxi32 iVer;         /* HTTP protocol version */
 48427  	 sxi32 rc;
 48428  	 SyStringInitFromBuf(&sRequest, zRequest, nByte);
 48429  	 SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader));
 48430  	 SyBlobInit(&sWorker, &pVm->sAllocator);
 48431  	 /* Ignore leading and trailing white spaces*/
 48432  	 SyStringFullTrim(&sRequest);
 48433  	 /* Process the first line */
 48434  	 rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer);
 48435  	 if( rc != SXRET_OK ){
 48436  		 return rc;
 48437  	 }
 48438  	 /* Process MIME headers */
 48439  	 VmHttpExtractHeaders(&sRequest, &sHeader);
 48440  	 /*
 48441  	  * Setup $_SERVER environments 
 48442  	  */
 48443  	 /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */
 48444  	 jx9_vm_config(pVm, 
 48445  		 JX9_VM_CONFIG_SERVER_ATTR, 
 48446  		 "SERVER_PROTOCOL", 
 48447  		 iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1", 
 48448  		 sizeof("HTTP/1.1")-1
 48449  		 );
 48450  	 /* 'REQUEST_METHOD':  Which request method was used to access the page */
 48451  	 jx9_vm_config(pVm, 
 48452  		 JX9_VM_CONFIG_SERVER_ATTR, 
 48453  		 "REQUEST_METHOD", 
 48454  		 iMethod == HTTP_METHOD_GET ?   "GET" : 
 48455  		 (iMethod == HTTP_METHOD_POST ? "POST":
 48456  		 (iMethod == HTTP_METHOD_PUT  ? "PUT" :
 48457  		 (iMethod == HTTP_METHOD_HEAD ?  "HEAD" : "OTHER"))), 
 48458  		 -1 /* Compute attribute length automatically */
 48459  		 );
 48460  	 if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){
 48461  		 pValue = &sUri.sQuery;
 48462  		 /* 'QUERY_STRING': The query string, if any, via which the page was accessed */
 48463  		 jx9_vm_config(pVm, 
 48464  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48465  			 "QUERY_STRING", 
 48466  			 pValue->zString, 
 48467  			 pValue->nByte
 48468  			 );
 48469  		 /* Decoded the raw query */
 48470  		 VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE);
 48471  	 }
 48472  	 /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */
 48473  	 pValue = &sUri.sRaw;
 48474  	 jx9_vm_config(pVm, 
 48475  		 JX9_VM_CONFIG_SERVER_ATTR, 
 48476  		 "REQUEST_URI", 
 48477  		 pValue->zString, 
 48478  		 pValue->nByte
 48479  		 );
 48480  	 /*
 48481  	  * 'PATH_INFO'
 48482  	  * 'ORIG_PATH_INFO' 
 48483        * Contains any client-provided pathname information trailing the actual script filename but preceding
 48484  	  * the query string, if available. For instance, if the current script was accessed via the URL
 48485  	  * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
 48486  	  * /some/stuff. 
 48487  	  */
 48488  	 pValue = &sUri.sPath;
 48489  	 jx9_vm_config(pVm, 
 48490  		 JX9_VM_CONFIG_SERVER_ATTR, 
 48491  		 "PATH_INFO", 
 48492  		 pValue->zString, 
 48493  		 pValue->nByte
 48494  		 );
 48495  	 jx9_vm_config(pVm, 
 48496  		 JX9_VM_CONFIG_SERVER_ATTR, 
 48497  		 "ORIG_PATH_INFO", 
 48498  		 pValue->zString, 
 48499  		 pValue->nByte
 48500  		 );
 48501  	 /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */
 48502  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1);
 48503  	 if( pValue ){
 48504  		 jx9_vm_config(pVm, 
 48505  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48506  			 "HTTP_ACCEPT", 
 48507  			 pValue->zString, 
 48508  			 pValue->nByte
 48509  		 );
 48510  	 }
 48511  	 /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */
 48512  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1);
 48513  	 if( pValue ){
 48514  		 jx9_vm_config(pVm, 
 48515  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48516  			 "HTTP_ACCEPT_CHARSET", 
 48517  			 pValue->zString, 
 48518  			 pValue->nByte
 48519  		 );
 48520  	 }
 48521  	 /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */
 48522  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1);
 48523  	 if( pValue ){
 48524  		 jx9_vm_config(pVm, 
 48525  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48526  			 "HTTP_ACCEPT_ENCODING", 
 48527  			 pValue->zString, 
 48528  			 pValue->nByte
 48529  		 );
 48530  	 }
 48531  	  /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */
 48532  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1);
 48533  	 if( pValue ){
 48534  		 jx9_vm_config(pVm, 
 48535  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48536  			 "HTTP_ACCEPT_LANGUAGE", 
 48537  			 pValue->zString, 
 48538  			 pValue->nByte
 48539  		 );
 48540  	 }
 48541  	 /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */
 48542  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1);
 48543  	 if( pValue ){
 48544  		 jx9_vm_config(pVm, 
 48545  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48546  			 "HTTP_CONNECTION", 
 48547  			 pValue->zString, 
 48548  			 pValue->nByte
 48549  		 );
 48550  	 }
 48551  	 /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */
 48552  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1);
 48553  	 if( pValue ){
 48554  		 jx9_vm_config(pVm, 
 48555  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48556  			 "HTTP_HOST", 
 48557  			 pValue->zString, 
 48558  			 pValue->nByte
 48559  		 );
 48560  	 }
 48561  	 /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */
 48562  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1);
 48563  	 if( pValue ){
 48564  		 jx9_vm_config(pVm, 
 48565  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48566  			 "HTTP_REFERER", 
 48567  			 pValue->zString, 
 48568  			 pValue->nByte
 48569  		 );
 48570  	 }
 48571  	 /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */
 48572  	 pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1);
 48573  	 if( pValue ){
 48574  		 jx9_vm_config(pVm, 
 48575  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48576  			 "HTTP_USER_AGENT", 
 48577  			 pValue->zString, 
 48578  			 pValue->nByte
 48579  		 );
 48580  	 }
 48581  	  /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization'
 48582  	   * header sent by the client (which you should then use to make the appropriate validation).
 48583  	   */
 48584  	 pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1);
 48585  	 if( pValue ){
 48586  		 jx9_vm_config(pVm, 
 48587  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48588  			 "JX9_AUTH_DIGEST", 
 48589  			 pValue->zString, 
 48590  			 pValue->nByte
 48591  		 );
 48592  		 jx9_vm_config(pVm, 
 48593  			 JX9_VM_CONFIG_SERVER_ATTR, 
 48594  			 "JX9_AUTH", 
 48595  			 pValue->zString, 
 48596  			 pValue->nByte
 48597  		 );
 48598  	 }
 48599  	 /* Install all clients HTTP headers in the $_HEADER superglobal */
 48600  	 pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1);
 48601  	 /* Iterate throw the available MIME headers*/
 48602  	 SySetResetCursor(&sHeader);
 48603  	 pHeader = 0; /* stupid cc warning */
 48604  	 while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){
 48605  		 pName  = &pHeader->sName;
 48606  		 pValue = &pHeader->sValue;
 48607  		 if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){
 48608  			 /* Insert the MIME header and it's associated value */
 48609  			 VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther, 
 48610  				 pName->zString, (int)pName->nByte, 
 48611  				 pValue->zString, (int)pValue->nByte
 48612  				 );
 48613  		 }
 48614  		 if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0 
 48615  			 && pValue->nByte > 0){
 48616  				 /* Process the name=value pair and insert them in the $_COOKIE superglobal array */
 48617  				 VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte);
 48618  		 }
 48619  	 }
 48620  	 if( iMethod == HTTP_METHOD_POST ){
 48621  		 /* Extract raw POST data */
 48622  		 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1);
 48623  		 if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 &&
 48624  			 SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){
 48625  				 /* Extract POST data length */
 48626  				 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1);
 48627  				 if( pValue ){
 48628  					 sxi32 iLen = 0; /* POST data length */
 48629  					 SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0);
 48630  					 if( iLen > 0 ){
 48631  						 /* Remove leading and trailing white spaces */
 48632  						 SyStringFullTrim(&sRequest);
 48633  						 if( (int)sRequest.nByte > iLen ){
 48634  							 sRequest.nByte = (sxu32)iLen;
 48635  						 }
 48636  						 /* Decode POST data now */
 48637  						 VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE);
 48638  					 }
 48639  				 }
 48640  		 }
 48641  	 }
 48642  	 /* All done, clean-up the mess left behind */
 48643  	 SySetRelease(&sHeader);
 48644  	 SyBlobRelease(&sWorker);
 48645  	 return SXRET_OK;
 48646   }
 48647  
 48648  /*
 48649   * ----------------------------------------------------------
 48650   * File: lhash_kv.c
 48651   * MD5: 581b07ce2984fd95740677285d8a11d3
 48652   * ----------------------------------------------------------
 48653   */
 48654  /*
 48655   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 48656   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 48657   * Version 1.1.6
 48658   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 48659   * please contact Symisc Systems via:
 48660   *       legal@symisc.net
 48661   *       licensing@symisc.net
 48662   *       contact@symisc.net
 48663   * or visit:
 48664   *      http://unqlite.org/licensing.html
 48665   */
 48666   /* $SymiscID: lhash_kv.c v1.7 Solaris 2013-01-14 12:56 stable <chm@symisc.net> $ */
 48667  #ifndef UNQLITE_AMALGAMATION
 48668  #include "unqliteInt.h"
 48669  #endif
 48670  /* 
 48671   * This file implements disk based hashtable using the linear hashing algorithm.
 48672   * This implementation is the one decribed in the paper:
 48673   *  LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France.
 48674   * Plus a smart extension called Virtual Bucket Table. (contact devel@symisc.net for additional information).
 48675   */
 48676  /* Magic number identifying a valid storage image */
 48677  #define L_HASH_MAGIC 0xFA782DCB
 48678  /*
 48679   * Magic word to hash to identify a valid hash function.
 48680   */
 48681  #define L_HASH_WORD "chm@symisc"
 48682  /*
 48683   * Cell size on disk. 
 48684   */
 48685  #define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/)
 48686  /*
 48687   * Primary page (not overflow pages) header size on disk.
 48688   */
 48689  #define L_HASH_PAGE_HDR_SZ (2/* Cell offset*/+2/* Free block offset*/+8/*Slave page number*/)
 48690  /*
 48691   * The maximum amount of payload (in bytes) that can be stored locally for
 48692   * a database entry.  If the entry contains more data than this, the
 48693   * extra goes onto overflow pages.
 48694  */
 48695  #define L_HASH_MX_PAYLOAD(PageSize)  (PageSize-(L_HASH_PAGE_HDR_SZ+L_HASH_CELL_SZ))
 48696  /*
 48697   * Maxium free space on a single page.
 48698   */
 48699  #define L_HASH_MX_FREE_SPACE(PageSize) (PageSize - (L_HASH_PAGE_HDR_SZ))
 48700  /*
 48701  ** The maximum number of bytes of payload allowed on a single overflow page.
 48702  */
 48703  #define L_HASH_OVERFLOW_SIZE(PageSize) (PageSize-8)
 48704  /* Forward declaration */
 48705  typedef struct lhash_kv_engine lhash_kv_engine;
 48706  typedef struct lhpage lhpage;
 48707  /*
 48708   * Each record in the database is identified either in-memory or in
 48709   * disk by an instance of the following structure.
 48710   */
 48711  typedef struct lhcell lhcell;
 48712  struct lhcell
 48713  {
 48714  	/* Disk-data (Big-Endian) */
 48715  	sxu32 nHash;   /* Hash of the key: 4 bytes */
 48716  	sxu32 nKey;    /* Key length: 4 bytes */
 48717  	sxu64 nData;   /* Data length: 8 bytes */
 48718  	sxu16 iNext;   /* Offset of the next cell: 2 bytes */
 48719  	pgno iOvfl;    /* Overflow page number if any: 8 bytes */
 48720  	/* In-memory data only */
 48721  	lhpage *pPage;     /* Page this cell belongs */
 48722  	sxu16 iStart;      /* Offset of this cell */
 48723  	pgno iDataPage;    /* Data page number when overflow */
 48724  	sxu16 iDataOfft;   /* Offset of the data in iDataPage */
 48725  	SyBlob sKey;       /* Record key for fast lookup (Kept in-memory if < 256KB ) */
 48726  	lhcell *pNext,*pPrev;         /* Linked list of the loaded memory cells */
 48727  	lhcell *pNextCol,*pPrevCol;   /* Collison chain  */
 48728  };
 48729  /*
 48730  ** Each database page has a header that is an instance of this
 48731  ** structure.
 48732  */
 48733  typedef struct lhphdr lhphdr;
 48734  struct lhphdr 
 48735  {
 48736    sxu16 iOfft; /* Offset of the first cell */
 48737    sxu16 iFree; /* Offset of the first free block*/
 48738    pgno iSlave; /* Slave page number */
 48739  };
 48740  /*
 48741   * Each loaded primary disk page is represented in-memory using
 48742   * an instance of the following structure.
 48743   */
 48744  struct lhpage
 48745  {
 48746  	lhash_kv_engine *pHash;  /* KV Storage engine that own this page */
 48747  	unqlite_page *pRaw;      /* Raw page contents */
 48748  	lhphdr sHdr;             /* Processed page header */
 48749  	lhcell **apCell;         /* Cell buckets */
 48750  	lhcell *pList,*pFirst;   /* Linked list of cells */
 48751  	sxu32 nCell;             /* Total number of cells */
 48752  	sxu32 nCellSize;         /* apCell[] size */
 48753  	lhpage *pMaster;         /* Master page in case we are dealing with a slave page */
 48754  	lhpage *pSlave;          /* List of slave pages */
 48755  	lhpage *pNextSlave;      /* Next slave page on the list */
 48756  	sxi32 iSlave;            /* Total number of slave pages */
 48757  	sxu16 nFree;             /* Amount of free space available in the page */
 48758  };
 48759  /*
 48760   * A Bucket map record which is used to map logical bucket number to real
 48761   * bucket number is represented by an instance of the following structure.
 48762   */
 48763  typedef struct lhash_bmap_rec lhash_bmap_rec;
 48764  struct lhash_bmap_rec
 48765  {
 48766  	pgno iLogic;                   /* Logical bucket number */
 48767  	pgno iReal;                    /* Real bucket number */
 48768  	lhash_bmap_rec *pNext,*pPrev;  /* Link to other bucket map */     
 48769  	lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */
 48770  };
 48771  typedef struct lhash_bmap_page lhash_bmap_page;
 48772  struct lhash_bmap_page
 48773  {
 48774  	pgno iNum;   /* Page number where this entry is stored */
 48775  	sxu16 iPtr;  /* Offset to start reading/writing from */
 48776  	sxu32 nRec;  /* Total number of records in this page */
 48777  	pgno iNext;  /* Next map page */
 48778  };
 48779  /*
 48780   * An in memory linear hash implemenation is represented by in an isntance
 48781   * of the following structure.
 48782   */
 48783  struct lhash_kv_engine
 48784  {
 48785  	const unqlite_kv_io *pIo;     /* IO methods: Must be first */
 48786  	/* Private fields */
 48787  	SyMemBackend sAllocator;      /* Private memory backend */
 48788  	ProcHash xHash;               /* Default hash function */
 48789  	ProcCmp xCmp;                 /* Default comparison function */
 48790  	unqlite_page *pHeader;        /* Page one to identify a valid implementation */
 48791  	lhash_bmap_rec **apMap;       /* Buckets map records */
 48792  	sxu32 nBuckRec;               /* Total number of bucket map records */
 48793  	sxu32 nBuckSize;              /* apMap[] size  */
 48794  	lhash_bmap_rec *pList;        /* List of bucket map records */
 48795  	lhash_bmap_rec *pFirst;       /* First record*/
 48796  	lhash_bmap_page sPageMap;     /* Primary bucket map */
 48797  	int iPageSize;                /* Page size */
 48798  	pgno nFreeList;               /* List of free pages */
 48799  	pgno split_bucket;            /* Current split bucket: MUST BE A POWER OF TWO */
 48800  	pgno max_split_bucket;        /* Maximum split bucket: MUST BE A POWER OF TWO */
 48801  	pgno nmax_split_nucket;       /* Next maximum split bucket (1 << nMsb): In-memory only */
 48802  	sxu32 nMagic;                 /* Magic number to identify a valid linear hash disk database */
 48803  };
 48804  /*
 48805   * Given a logical bucket number, return the record associated with it.
 48806   */
 48807  static lhash_bmap_rec * lhMapFindBucket(lhash_kv_engine *pEngine,pgno iLogic)
 48808  {
 48809  	lhash_bmap_rec *pRec;
 48810  	if( pEngine->nBuckRec < 1 ){
 48811  		/* Don't bother */
 48812  		return 0;
 48813  	}
 48814  	pRec = pEngine->apMap[iLogic & (pEngine->nBuckSize - 1)];
 48815  	for(;;){
 48816  		if( pRec == 0 ){
 48817  			break;
 48818  		}
 48819  		if( pRec->iLogic == iLogic ){
 48820  			return pRec;
 48821  		}
 48822  		/* Point to the next entry */
 48823  		pRec = pRec->pNextCol;
 48824  	}
 48825  	/* No such record */
 48826  	return 0;
 48827  }
 48828  /*
 48829   * Install a new bucket map record.
 48830   */
 48831  static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
 48832  {
 48833  	lhash_bmap_rec *pRec;
 48834  	sxu32 iBucket;
 48835  	/* Allocate a new instance */
 48836  	pRec = (lhash_bmap_rec *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhash_bmap_rec));
 48837  	if( pRec == 0 ){
 48838  		return UNQLITE_NOMEM;
 48839  	}
 48840  	/* Zero the structure */
 48841  	SyZero(pRec,sizeof(lhash_bmap_rec));
 48842  	/* Fill in the structure */
 48843  	pRec->iLogic = iLogic;
 48844  	pRec->iReal = iReal;
 48845  	iBucket = iLogic & (pEngine->nBuckSize - 1);
 48846  	pRec->pNextCol = pEngine->apMap[iBucket];
 48847  	if( pEngine->apMap[iBucket] ){
 48848  		pEngine->apMap[iBucket]->pPrevCol = pRec;
 48849  	}
 48850  	pEngine->apMap[iBucket] = pRec;
 48851  	/* Link */
 48852  	if( pEngine->pFirst == 0 ){
 48853  		pEngine->pFirst = pEngine->pList = pRec;
 48854  	}else{
 48855  		MACRO_LD_PUSH(pEngine->pList,pRec);
 48856  	}
 48857  	pEngine->nBuckRec++;
 48858  	if( (pEngine->nBuckRec >= pEngine->nBuckSize * 3) && pEngine->nBuckRec < 100000 ){
 48859  		/* Allocate a new larger table */
 48860  		sxu32 nNewSize = pEngine->nBuckSize << 1;
 48861  		lhash_bmap_rec *pEntry;
 48862  		lhash_bmap_rec **apNew;
 48863  		sxu32 n;
 48864  		
 48865  		apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *));
 48866  		if( apNew ){
 48867  			/* Zero the new table */
 48868  			SyZero((void *)apNew, nNewSize * sizeof(lhash_bmap_rec *));
 48869  			/* Rehash all entries */
 48870  			n = 0;
 48871  			pEntry = pEngine->pList;
 48872  			for(;;){
 48873  				/* Loop one */
 48874  				if( n >= pEngine->nBuckRec ){
 48875  					break;
 48876  				}
 48877  				pEntry->pNextCol = pEntry->pPrevCol = 0;
 48878  				/* Install in the new bucket */
 48879  				iBucket = pEntry->iLogic & (nNewSize - 1);
 48880  				pEntry->pNextCol = apNew[iBucket];
 48881  				if( apNew[iBucket] ){
 48882  					apNew[iBucket]->pPrevCol = pEntry;
 48883  				}
 48884  				apNew[iBucket] = pEntry;
 48885  				/* Point to the next entry */
 48886  				pEntry = pEntry->pNext;
 48887  				n++;
 48888  			}
 48889  			/* Release the old table and reflect the change */
 48890  			SyMemBackendFree(&pEngine->sAllocator,(void *)pEngine->apMap);
 48891  			pEngine->apMap = apNew;
 48892  			pEngine->nBuckSize  = nNewSize;
 48893  		}
 48894  	}
 48895  	return UNQLITE_OK;
 48896  }
 48897  /*
 48898   * Process a raw bucket map record.
 48899   */
 48900  static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const unsigned char *zRaw)
 48901  {
 48902  	const unsigned char *zEnd = &zRaw[pEngine->iPageSize];
 48903  	const unsigned char *zPtr = zRaw;
 48904  	pgno iLogic,iReal;
 48905  	sxu32 n;
 48906  	int rc;
 48907  	if( pMap->iPtr == 0 ){
 48908  		/* Read the map header */
 48909  		SyBigEndianUnpack64(zRaw,&pMap->iNext);
 48910  		zRaw += 8;
 48911  		SyBigEndianUnpack32(zRaw,&pMap->nRec);
 48912  		zRaw += 4;
 48913  	}else{
 48914  		/* Mostly page one of the database */
 48915  		zRaw += pMap->iPtr;
 48916  	}
 48917  	/* Start processing */
 48918  	for( n = 0; n < pMap->nRec ; ++n ){
 48919  		if( zRaw >= zEnd ){
 48920  			break;
 48921  		}
 48922  		/* Extract the logical and real bucket number */
 48923  		SyBigEndianUnpack64(zRaw,&iLogic);
 48924  		zRaw += 8;
 48925  		SyBigEndianUnpack64(zRaw,&iReal);
 48926  		zRaw += 8;
 48927  		/* Install the record in the map */
 48928  		rc = lhMapInstallBucket(pEngine,iLogic,iReal);
 48929  		if( rc != UNQLITE_OK ){
 48930  			return rc;
 48931  		}
 48932  	}
 48933  	pMap->iPtr = (sxu16)(zRaw-zPtr);
 48934  	/* All done */
 48935  	return UNQLITE_OK;
 48936  }
 48937  /* 
 48938   * Allocate a new cell instance.
 48939   */
 48940  static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage)
 48941  {
 48942  	lhcell *pCell;
 48943  	pCell = (lhcell *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhcell));
 48944  	if( pCell == 0 ){
 48945  		return 0;
 48946  	}
 48947  	/* Zero the structure */
 48948  	SyZero(pCell,sizeof(lhcell));
 48949  	/* Fill in the structure */
 48950  	SyBlobInit(&pCell->sKey,&pEngine->sAllocator);
 48951  	pCell->pPage = pPage;
 48952  	return pCell;
 48953  }
 48954  /*
 48955   * Discard a cell from the page table.
 48956   */
 48957  static void lhCellDiscard(lhcell *pCell)
 48958  {
 48959  	lhpage *pPage = pCell->pPage->pMaster;	
 48960  	
 48961  	if( pCell->pPrevCol ){
 48962  		pCell->pPrevCol->pNextCol = pCell->pNextCol;
 48963  	}else{
 48964  		pPage->apCell[pCell->nHash & (pPage->nCellSize - 1)] = pCell->pNextCol;
 48965  	}
 48966  	if( pCell->pNextCol ){
 48967  		pCell->pNextCol->pPrevCol = pCell->pPrevCol;
 48968  	}
 48969  	MACRO_LD_REMOVE(pPage->pList,pCell);
 48970  	if( pCell == pPage->pFirst ){
 48971  		pPage->pFirst = pCell->pPrev;
 48972  	}
 48973  	pPage->nCell--;
 48974  	/* Release the cell */
 48975  	SyBlobRelease(&pCell->sKey);
 48976  	SyMemBackendPoolFree(&pPage->pHash->sAllocator,pCell);
 48977  }
 48978  /*
 48979   * Install a cell in the page table.
 48980   */
 48981  static int lhInstallCell(lhcell *pCell)
 48982  {
 48983  	lhpage *pPage = pCell->pPage->pMaster;
 48984  	sxu32 iBucket;
 48985  	if( pPage->nCell < 1 ){
 48986  		sxu32 nTableSize = 32; /* Must be a power of two */
 48987  		lhcell **apTable;
 48988  		/* Allocate a new cell table */
 48989  		apTable = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nTableSize * sizeof(lhcell *));
 48990  		if( apTable == 0 ){
 48991  			return UNQLITE_NOMEM;
 48992  		}
 48993  		/* Zero the new table */
 48994  		SyZero((void *)apTable, nTableSize * sizeof(lhcell *));
 48995  		/* Install it */
 48996  		pPage->apCell = apTable;
 48997  		pPage->nCellSize = nTableSize;
 48998  	}
 48999  	iBucket = pCell->nHash & (pPage->nCellSize - 1);
 49000  	pCell->pNextCol = pPage->apCell[iBucket];
 49001  	if( pPage->apCell[iBucket] ){
 49002  		pPage->apCell[iBucket]->pPrevCol = pCell;
 49003  	}
 49004  	pPage->apCell[iBucket] = pCell;
 49005  	if( pPage->pFirst == 0 ){
 49006  		pPage->pFirst = pPage->pList = pCell;
 49007  	}else{
 49008  		MACRO_LD_PUSH(pPage->pList,pCell);
 49009  	}
 49010  	pPage->nCell++;
 49011  	if( (pPage->nCell >= pPage->nCellSize * 3) && pPage->nCell < 100000 ){
 49012  		/* Allocate a new larger table */
 49013  		sxu32 nNewSize = pPage->nCellSize << 1;
 49014  		lhcell *pEntry;
 49015  		lhcell **apNew;
 49016  		sxu32 n;
 49017  		
 49018  		apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *));
 49019  		if( apNew ){
 49020  			/* Zero the new table */
 49021  			SyZero((void *)apNew, nNewSize * sizeof(lhcell *));
 49022  			/* Rehash all entries */
 49023  			n = 0;
 49024  			pEntry = pPage->pList;
 49025  			for(;;){
 49026  				/* Loop one */
 49027  				if( n >= pPage->nCell ){
 49028  					break;
 49029  				}
 49030  				pEntry->pNextCol = pEntry->pPrevCol = 0;
 49031  				/* Install in the new bucket */
 49032  				iBucket = pEntry->nHash & (nNewSize - 1);
 49033  				pEntry->pNextCol = apNew[iBucket];
 49034  				if( apNew[iBucket]  ){
 49035  					apNew[iBucket]->pPrevCol = pEntry;
 49036  				}
 49037  				apNew[iBucket] = pEntry;
 49038  				/* Point to the next entry */
 49039  				pEntry = pEntry->pNext;
 49040  				n++;
 49041  			}
 49042  			/* Release the old table and reflect the change */
 49043  			SyMemBackendFree(&pPage->pHash->sAllocator,(void *)pPage->apCell);
 49044  			pPage->apCell = apNew;
 49045  			pPage->nCellSize  = nNewSize;
 49046  		}
 49047  	}
 49048  	return UNQLITE_OK;
 49049  }
 49050  /*
 49051   * Private data of lhKeyCmp().
 49052   */
 49053  struct lhash_key_cmp
 49054  {
 49055  	const char *zIn;  /* Start of the stream */
 49056  	const char *zEnd; /* End of the stream */
 49057  	ProcCmp xCmp;     /* Comparison function */
 49058  };
 49059  /*
 49060   * Comparsion callback for large key > 256 KB
 49061   */
 49062  static int lhKeyCmp(const void *pData,sxu32 nLen,void *pUserData)
 49063  {
 49064  	struct lhash_key_cmp *pCmp = (struct lhash_key_cmp *)pUserData;
 49065  	int rc;
 49066  	if( pCmp->zIn >= pCmp->zEnd ){
 49067  		if( nLen > 0 ){
 49068  			return UNQLITE_ABORT;
 49069  		}
 49070  		return UNQLITE_OK;
 49071  	}
 49072  	/* Perform the comparison */
 49073  	rc = pCmp->xCmp((const void *)pCmp->zIn,pData,nLen);
 49074  	if( rc != 0 ){
 49075  		/* Abort comparison */
 49076  		return UNQLITE_ABORT;
 49077  	}
 49078  	/* Advance the cursor */
 49079  	pCmp->zIn += nLen;
 49080  	return UNQLITE_OK;
 49081  }
 49082  /* Forward declaration */
 49083  static int lhConsumeCellkey(lhcell *pCell,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData,int offt_only);
 49084  /*
 49085   * given a key, return the cell associated with it on success. NULL otherwise.
 49086   */
 49087  static lhcell * lhFindCell(
 49088  	lhpage *pPage,    /* Target page */
 49089  	const void *pKey, /* Lookup key */
 49090  	sxu32 nByte,      /* Key length */
 49091  	sxu32 nHash       /* Hash of the key */
 49092  	)
 49093  {
 49094  	lhcell *pEntry;
 49095  	if( pPage->nCell < 1 ){
 49096  		/* Don't bother hashing */
 49097  		return 0;
 49098  	}
 49099  	/* Point to the corresponding bucket */
 49100  	pEntry = pPage->apCell[nHash & (pPage->nCellSize - 1)];
 49101  	for(;;){
 49102  		if( pEntry == 0 ){
 49103  			break;
 49104  		}
 49105  		if( pEntry->nHash == nHash && pEntry->nKey == nByte ){
 49106  			if( SyBlobLength(&pEntry->sKey) < 1 ){
 49107  				/* Large key (> 256 KB) are not kept in-memory */
 49108  				struct lhash_key_cmp sCmp;
 49109  				int rc;
 49110  				/* Fill-in the structure */
 49111  				sCmp.zIn = (const char *)pKey;
 49112  				sCmp.zEnd = &sCmp.zIn[nByte];
 49113  				sCmp.xCmp = pPage->pHash->xCmp;
 49114  				/* Fetch the key from disk and perform the comparison */
 49115  				rc = lhConsumeCellkey(pEntry,lhKeyCmp,&sCmp,0);
 49116  				if( rc == UNQLITE_OK ){
 49117  					/* Cell found */
 49118  					return pEntry;
 49119  				}
 49120  			}else if ( pPage->pHash->xCmp(pKey,SyBlobData(&pEntry->sKey),nByte) == 0 ){
 49121  				/* Cell found */
 49122  				return pEntry;
 49123  			}
 49124  		}
 49125  		/* Point to the next entry */
 49126  		pEntry = pEntry->pNextCol;
 49127  	}
 49128  	/* No such entry */
 49129  	return 0;
 49130  }
 49131  /*
 49132   * Parse a raw cell fetched from disk.
 49133   */
 49134  static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned char *zEnd,lhcell **ppOut)
 49135  {
 49136  	sxu16 iNext,iOfft;
 49137  	sxu32 iHash,nKey;
 49138  	lhcell *pCell;
 49139  	sxu64 nData;
 49140  	int rc;
 49141  	/* Offset this cell is stored */
 49142  	iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData);
 49143  	/* 4 byte hash number */
 49144  	SyBigEndianUnpack32(zRaw,&iHash);
 49145  	zRaw += 4;	
 49146  	/* 4 byte key length  */
 49147  	SyBigEndianUnpack32(zRaw,&nKey);
 49148  	zRaw += 4;	
 49149  	/* 8 byte data length */
 49150  	SyBigEndianUnpack64(zRaw,&nData);
 49151  	zRaw += 8;
 49152  	/* 2 byte offset of the next cell */
 49153  	SyBigEndianUnpack16(zRaw,&iNext);
 49154  	/* Perform a sanity check */
 49155  	if( iNext > 0 && &pPage->pRaw->zData[iNext] >= zEnd ){
 49156  		return UNQLITE_CORRUPT;
 49157  	}
 49158  	zRaw += 2;
 49159  	pCell = lhNewCell(pPage->pHash,pPage);
 49160  	if( pCell == 0 ){
 49161  		return UNQLITE_NOMEM;
 49162  	}
 49163  	/* Fill in the structure */
 49164  	pCell->iNext = iNext;
 49165  	pCell->nKey  = nKey;
 49166  	pCell->nData = nData;
 49167  	pCell->nHash = iHash;
 49168  	/* Overflow page if any */
 49169  	SyBigEndianUnpack64(zRaw,&pCell->iOvfl);
 49170  	zRaw += 8;
 49171  	/* Cell offset */
 49172  	pCell->iStart = iOfft;
 49173  	/* Consume the key */
 49174  	rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,pCell->nKey > 262144 /* 256 KB */? 1 : 0);
 49175  	if( rc != UNQLITE_OK ){
 49176  		/* TICKET: 14-32-chm@symisc.net: Key too large for memory */
 49177  		SyBlobRelease(&pCell->sKey);
 49178  	}
 49179  	/* Finally install the cell */
 49180  	rc = lhInstallCell(pCell);
 49181  	if( rc != UNQLITE_OK ){
 49182  		return rc;
 49183  	}
 49184  	if( ppOut ){
 49185  		*ppOut = pCell;
 49186  	}
 49187  	return UNQLITE_OK;
 49188  }
 49189  /*
 49190   * Compute the total number of free space on a given page.
 49191   */
 49192  static int lhPageFreeSpace(lhpage *pPage)
 49193  {
 49194  	const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
 49195  	lhphdr *pHdr = &pPage->sHdr;
 49196  	sxu16 iNext,iAmount;
 49197  	sxu16 nFree = 0;
 49198  	if( pHdr->iFree < 1 ){
 49199  		/* Don't bother processing, the page is full */
 49200  		pPage->nFree = 0;
 49201  		return UNQLITE_OK;
 49202  	}
 49203  	/* Point to first free block */
 49204  	zEnd = &zRaw[pPage->pHash->iPageSize];
 49205  	zRaw += pHdr->iFree;
 49206  	for(;;){
 49207  		/* Offset of the next free block */
 49208  		SyBigEndianUnpack16(zRaw,&iNext);
 49209  		zRaw += 2;
 49210  		/* Available space on this block */
 49211  		SyBigEndianUnpack16(zRaw,&iAmount);
 49212  		nFree += iAmount;
 49213  		if( iNext < 1 ){
 49214  			/* No more free blocks */
 49215  			break;
 49216  		}
 49217  		/* Point to the next free block*/
 49218  		zRaw = &pPage->pRaw->zData[iNext];
 49219  		if( zRaw >= zEnd ){
 49220  			/* Corrupt page */
 49221  			return UNQLITE_CORRUPT;
 49222  		}
 49223  	}
 49224  	/* Save the amount of free space */
 49225  	pPage->nFree = nFree;
 49226  	return UNQLITE_OK;
 49227  }
 49228  /*
 49229   * Given a primary page, load all its cell.
 49230   */
 49231  static int lhLoadCells(lhpage *pPage)
 49232  {
 49233  	const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
 49234  	lhphdr *pHdr = &pPage->sHdr;
 49235  	lhcell *pCell = 0; /* cc warning */
 49236  	int rc;
 49237  	/* Calculate the amount of free space available first */
 49238  	rc = lhPageFreeSpace(pPage);
 49239  	if( rc != UNQLITE_OK ){
 49240  		return rc;
 49241  	}
 49242  	if( pHdr->iOfft < 1 ){
 49243  		/* Don't bother processing, the page is empty */
 49244  		return UNQLITE_OK;
 49245  	}
 49246  	/* Point to first cell */
 49247  	zRaw += pHdr->iOfft;
 49248  	zEnd = &zRaw[pPage->pHash->iPageSize];
 49249  	for(;;){
 49250  		/* Parse a single cell */
 49251  		rc = lhParseOneCell(pPage,zRaw,zEnd,&pCell);
 49252  		if( rc != UNQLITE_OK ){
 49253  			return rc;
 49254  		}
 49255  		if( pCell->iNext < 1 ){
 49256  			/* No more cells */
 49257  			break;
 49258  		}
 49259  		/* Point to the next cell */
 49260  		zRaw = &pPage->pRaw->zData[pCell->iNext];
 49261  		if( zRaw >= zEnd ){
 49262  			/* Corrupt page */
 49263  			return UNQLITE_CORRUPT;
 49264  		}
 49265  	}
 49266  	/* All done */
 49267  	return UNQLITE_OK;
 49268  }
 49269  /*
 49270   * Given a page, parse its raw headers.
 49271   */
 49272  static int lhParsePageHeader(lhpage *pPage)
 49273  {
 49274  	const unsigned char *zRaw = pPage->pRaw->zData;
 49275  	lhphdr *pHdr = &pPage->sHdr;
 49276  	/* Offset of the first cell */
 49277  	SyBigEndianUnpack16(zRaw,&pHdr->iOfft);
 49278  	zRaw += 2;
 49279  	/* Offset of the first free block */
 49280  	SyBigEndianUnpack16(zRaw,&pHdr->iFree);
 49281  	zRaw += 2;
 49282  	/* Slave page number */
 49283  	SyBigEndianUnpack64(zRaw,&pHdr->iSlave);
 49284  	/* All done */
 49285  	return UNQLITE_OK;
 49286  }
 49287  /*
 49288   * Allocate a new page instance.
 49289   */
 49290  static lhpage * lhNewPage(
 49291  	lhash_kv_engine *pEngine, /* KV store which own this instance */
 49292  	unqlite_page *pRaw,       /* Raw page contents */
 49293  	lhpage *pMaster           /* Master page in case we are dealing with a slave page */
 49294  	)
 49295  {
 49296  	lhpage *pPage;
 49297  	/* Allocate a new instance */
 49298  	pPage = (lhpage *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhpage));
 49299  	if( pPage == 0 ){
 49300  		return 0;
 49301  	}
 49302  	/* Zero the structure */
 49303  	SyZero(pPage,sizeof(lhpage));
 49304  	/* Fill-in the structure */
 49305  	pPage->pHash = pEngine;
 49306  	pPage->pRaw = pRaw;
 49307  	pPage->pMaster = pMaster ? pMaster /* Slave page */ : pPage /* Master page */ ;
 49308  	if( pPage->pMaster != pPage ){
 49309  		/* Slave page, attach it to its master */
 49310  		pPage->pNextSlave = pMaster->pSlave;
 49311  		pMaster->pSlave = pPage;
 49312  		pMaster->iSlave++;
 49313  	}
 49314  	/* Save this instance for future fast lookup */
 49315  	pRaw->pUserData = pPage;
 49316  	/* All done */
 49317  	return pPage;
 49318  }
 49319  /*
 49320   * Load a primary and its associated slave pages from disk.
 49321   */
 49322  static int lhLoadPage(lhash_kv_engine *pEngine,pgno pnum,lhpage *pMaster,lhpage **ppOut,int iNest)
 49323  {
 49324  	unqlite_page *pRaw;
 49325  	lhpage *pPage = 0; /* cc warning */
 49326  	int rc;
 49327  	/* Aquire the page from the pager first */
 49328  	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pnum,&pRaw);
 49329  	if( rc != UNQLITE_OK ){
 49330  		return rc;
 49331  	}
 49332  	if( pRaw->pUserData ){
 49333  		/* The page is already parsed and loaded in memory. Point to it */
 49334  		pPage = (lhpage *)pRaw->pUserData;
 49335  	}else{
 49336  		/* Allocate a new page */
 49337  		pPage = lhNewPage(pEngine,pRaw,pMaster);
 49338  		if( pPage == 0 ){
 49339  			return UNQLITE_NOMEM;
 49340  		}
 49341  		/* Process the page */
 49342  		rc = lhParsePageHeader(pPage);
 49343  		if( rc == UNQLITE_OK ){
 49344  			/* Load cells */
 49345  			rc = lhLoadCells(pPage);
 49346  		}
 49347  		if( rc != UNQLITE_OK ){
 49348  			pEngine->pIo->xPageUnref(pPage->pRaw); /* pPage will be released inside this call */
 49349  			return rc;
 49350  		}
 49351  		if( pPage->sHdr.iSlave > 0 && iNest < 128 ){
 49352  			if( pMaster == 0 ){
 49353  				pMaster = pPage;
 49354  			}
 49355  			/* Slave page. Not a fatal error if something goes wrong here */
 49356  			lhLoadPage(pEngine,pPage->sHdr.iSlave,pMaster,0,iNest++);
 49357  		}
 49358  	}
 49359  	if( ppOut ){
 49360  		*ppOut = pPage;
 49361  	}
 49362  	return UNQLITE_OK;
 49363  }
 49364  /*
 49365   * Given a cell, Consume its key by invoking the given callback for each extracted chunk.
 49366   */
 49367  static int lhConsumeCellkey(
 49368  	lhcell *pCell, /* Target cell */
 49369  	int (*xConsumer)(const void *,unsigned int,void *), /* Consumer callback */
 49370  	void *pUserData, /* Last argument to xConsumer() */
 49371  	int offt_only
 49372  	)
 49373  {
 49374  	lhpage *pPage = pCell->pPage;
 49375  	const unsigned char *zRaw = pPage->pRaw->zData;
 49376  	const unsigned char *zPayload;
 49377  	int rc;
 49378  	/* Point to the payload area */
 49379  	zPayload = &zRaw[pCell->iStart];
 49380  	if( pCell->iOvfl == 0 ){
 49381  		/* Best scenario, consume the key directly without any overflow page */
 49382  		zPayload += L_HASH_CELL_SZ;
 49383  		rc = xConsumer((const void *)zPayload,pCell->nKey,pUserData);
 49384  		if( rc != UNQLITE_OK ){
 49385  			rc = UNQLITE_ABORT;
 49386  		}
 49387  	}else{
 49388  		lhash_kv_engine *pEngine = pPage->pHash;
 49389  		sxu32 nByte,nData = pCell->nKey;
 49390  		unqlite_page *pOvfl;
 49391  		int data_offset = 0;
 49392  		pgno iOvfl;
 49393  		/* Overflow page */
 49394  		iOvfl = pCell->iOvfl;
 49395  		/* Total usable bytes in an overflow page */
 49396  		nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
 49397  		for(;;){
 49398  			if( iOvfl == 0 || nData < 1 ){
 49399  				/* no more overflow page */
 49400  				break;
 49401  			}
 49402  			/* Point to the overflow page */
 49403  			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
 49404  			if( rc != UNQLITE_OK ){
 49405  				return rc;
 49406  			}
 49407  			zPayload = &pOvfl->zData[8];
 49408  			/* Point to the raw content */
 49409  			if( !data_offset ){
 49410  				/* Get the data page and offset */
 49411  				SyBigEndianUnpack64(zPayload,&pCell->iDataPage);
 49412  				zPayload += 8;
 49413  				SyBigEndianUnpack16(zPayload,&pCell->iDataOfft);
 49414  				zPayload += 2;
 49415  				if( offt_only ){
 49416  					/* Key too large, grab the data offset and return */
 49417  					pEngine->pIo->xPageUnref(pOvfl);
 49418  					return UNQLITE_OK;
 49419  				}
 49420  				data_offset = 1;
 49421  			}
 49422  			/* Consume the key */
 49423  			if( nData <= nByte ){
 49424  				rc = xConsumer((const void *)zPayload,nData,pUserData);
 49425  				if( rc != UNQLITE_OK ){
 49426  					pEngine->pIo->xPageUnref(pOvfl);
 49427  					return UNQLITE_ABORT;
 49428  				}
 49429  				nData = 0;
 49430  			}else{
 49431  				rc = xConsumer((const void *)zPayload,nByte,pUserData);
 49432  				if( rc != UNQLITE_OK ){
 49433  					pEngine->pIo->xPageUnref(pOvfl);
 49434  					return UNQLITE_ABORT;
 49435  				}
 49436  				nData -= nByte;
 49437  			}
 49438  			/* Next overflow page in the chain */
 49439  			SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
 49440  			/* Unref the page */
 49441  			pEngine->pIo->xPageUnref(pOvfl);
 49442  		}
 49443  		rc = UNQLITE_OK;
 49444  	}
 49445  	return rc;
 49446  }
 49447  /*
 49448   * Given a cell, Consume its data by invoking the given callback for each extracted chunk.
 49449   */
 49450  static int lhConsumeCellData(
 49451  	lhcell *pCell, /* Target cell */
 49452  	int (*xConsumer)(const void *,unsigned int,void *), /* Data consumer callback */
 49453  	void *pUserData /* Last argument to xConsumer() */
 49454  	)
 49455  {
 49456  	lhpage *pPage = pCell->pPage;
 49457  	const unsigned char *zRaw = pPage->pRaw->zData;
 49458  	const unsigned char *zPayload;
 49459  	int rc;
 49460  	/* Point to the payload area */
 49461  	zPayload = &zRaw[pCell->iStart];
 49462  	if( pCell->iOvfl == 0 ){
 49463  		/* Best scenario, consume the data directly without any overflow page */
 49464  		zPayload += L_HASH_CELL_SZ + pCell->nKey;
 49465  		rc = xConsumer((const void *)zPayload,(sxu32)pCell->nData,pUserData);
 49466  		if( rc != UNQLITE_OK ){
 49467  			rc = UNQLITE_ABORT;
 49468  		}
 49469  	}else{
 49470  		lhash_kv_engine *pEngine = pPage->pHash;
 49471  		sxu64 nData = pCell->nData;
 49472  		unqlite_page *pOvfl;
 49473  		int fix_offset = 0;
 49474  		sxu32 nByte;
 49475  		pgno iOvfl;
 49476  		/* Overflow page where data is stored */
 49477  		iOvfl = pCell->iDataPage;
 49478  		for(;;){
 49479  			if( iOvfl == 0 || nData < 1 ){
 49480  				/* no more overflow page */
 49481  				break;
 49482  			}
 49483  			/* Point to the overflow page */
 49484  			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
 49485  			if( rc != UNQLITE_OK ){
 49486  				return rc;
 49487  			}
 49488  			/* Point to the raw content */
 49489  			zPayload = pOvfl->zData;
 49490  			if( !fix_offset ){
 49491  				/* Point to the data */
 49492  				zPayload += pCell->iDataOfft;
 49493  				nByte = pEngine->iPageSize - pCell->iDataOfft;
 49494  				fix_offset = 1;
 49495  			}else{
 49496  				zPayload += 8;
 49497  				/* Total usable bytes in an overflow page */
 49498  				nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
 49499  			}
 49500  			/* Consume the data */
 49501  			if( nData <= (sxu64)nByte ){
 49502  				rc = xConsumer((const void *)zPayload,(unsigned int)nData,pUserData);
 49503  				if( rc != UNQLITE_OK ){
 49504  					pEngine->pIo->xPageUnref(pOvfl);
 49505  					return UNQLITE_ABORT;
 49506  				}
 49507  				nData = 0;
 49508  			}else{
 49509  				if( nByte > 0 ){
 49510  					rc = xConsumer((const void *)zPayload,nByte,pUserData);
 49511  					if( rc != UNQLITE_OK ){
 49512  						pEngine->pIo->xPageUnref(pOvfl);
 49513  						return UNQLITE_ABORT;
 49514  					}
 49515  					nData -= nByte;
 49516  				}
 49517  			}
 49518  			/* Next overflow page in the chain */
 49519  			SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
 49520  			/* Unref the page */
 49521  			pEngine->pIo->xPageUnref(pOvfl);
 49522  		}
 49523  		rc = UNQLITE_OK;
 49524  	}
 49525  	return rc;
 49526  }
 49527  /*
 49528   * Read the linear hash header (Page one of the database).
 49529   */
 49530  static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
 49531  {
 49532  	const unsigned char *zRaw = pHeader->zData;
 49533  	lhash_bmap_page *pMap;
 49534  	sxu32 nHash;
 49535  	int rc;
 49536  	pEngine->pHeader = pHeader;
 49537  	/* 4 byte magic number */
 49538  	SyBigEndianUnpack32(zRaw,&pEngine->nMagic);
 49539  	zRaw += 4;
 49540  	if( pEngine->nMagic != L_HASH_MAGIC ){
 49541  		/* Corrupt implementation */
 49542  		return UNQLITE_CORRUPT;
 49543  	}
 49544  	/* 4 byte hash value to identify a valid hash function */
 49545  	SyBigEndianUnpack32(zRaw,&nHash);
 49546  	zRaw += 4;
 49547  	/* Sanity check */
 49548  	if( pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1) != nHash ){
 49549  		/* Different hash function */
 49550  		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Invalid hash function");
 49551  		return UNQLITE_INVALID;
 49552  	}
 49553  	/* List of free pages */
 49554  	SyBigEndianUnpack64(zRaw,&pEngine->nFreeList);
 49555  	zRaw += 8;
 49556  	/* Current split bucket */
 49557  	SyBigEndianUnpack64(zRaw,&pEngine->split_bucket);
 49558  	zRaw += 8;
 49559  	/* Maximum split bucket */
 49560  	SyBigEndianUnpack64(zRaw,&pEngine->max_split_bucket);
 49561  	zRaw += 8;
 49562  	/* Next generation */
 49563  	pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1;
 49564  	/* Initialiaze the bucket map */
 49565  	pMap = &pEngine->sPageMap;
 49566  	/* Fill in the structure */
 49567  	pMap->iNum = pHeader->pgno;
 49568  	/* Next page in the bucket map */
 49569  	SyBigEndianUnpack64(zRaw,&pMap->iNext);
 49570  	zRaw += 8;
 49571  	/* Total number of records in the bucket map (This page only) */
 49572  	SyBigEndianUnpack32(zRaw,&pMap->nRec);
 49573  	zRaw += 4;
 49574  	pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
 49575  	/* Load the map in memory */
 49576  	rc = lhMapLoadPage(pEngine,pMap,pHeader->zData);
 49577  	if( rc != UNQLITE_OK ){
 49578  		return rc;
 49579  	}
 49580  	/* Load the bucket map chain if any */
 49581  	for(;;){
 49582  		pgno iNext = pMap->iNext;
 49583  		unqlite_page *pPage;
 49584  		if( iNext == 0 ){
 49585  			/* No more map pages */
 49586  			break;
 49587  		}
 49588  		/* Point to the target page */
 49589  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pPage);
 49590  		if( rc != UNQLITE_OK ){
 49591  			return rc;
 49592  		}
 49593  		/* Fill in the structure */
 49594  		pMap->iNum = iNext;
 49595  		pMap->iPtr = 0;
 49596  		/* Load the map in memory */
 49597  		rc = lhMapLoadPage(pEngine,pMap,pPage->zData);
 49598  		if( rc != UNQLITE_OK ){
 49599  			return rc;
 49600  		}
 49601  	}
 49602  	/* All done */
 49603  	return UNQLITE_OK;
 49604  }
 49605  /*
 49606   * Perform a record lookup.
 49607   */
 49608  static int lhRecordLookup(
 49609  	lhash_kv_engine *pEngine, /* KV storage engine */
 49610  	const void *pKey,         /* Lookup key */
 49611  	sxu32 nByte,              /* Key length */
 49612  	lhcell **ppCell           /* OUT: Target cell on success */
 49613  	)
 49614  {
 49615  	lhash_bmap_rec *pRec;
 49616  	lhpage *pPage;
 49617  	lhcell *pCell;
 49618  	pgno iBucket;
 49619  	sxu32 nHash;
 49620  	int rc;
 49621  	/* Acquire the first page (hash Header) so that everything gets loaded autmatically */
 49622  	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
 49623  	if( rc != UNQLITE_OK ){
 49624  		return rc;
 49625  	}
 49626  	/* Compute the hash of the key first */
 49627  	nHash = pEngine->xHash(pKey,nByte);
 49628  	/* Extract the logical (i.e. not real) page number */
 49629  	iBucket = nHash & (pEngine->nmax_split_nucket - 1);
 49630  	if( iBucket >= (pEngine->split_bucket + pEngine->max_split_bucket) ){
 49631  		/* Low mask */
 49632  		iBucket = nHash & (pEngine->max_split_bucket - 1);
 49633  	}
 49634  	/* Map the logical bucket number to real page number */
 49635  	pRec = lhMapFindBucket(pEngine,iBucket);
 49636  	if( pRec == 0 ){
 49637  		/* No such entry */
 49638  		return UNQLITE_NOTFOUND;
 49639  	}
 49640  	/* Load the master page and it's slave page in-memory  */
 49641  	rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
 49642  	if( rc != UNQLITE_OK ){
 49643  		/* IO error, unlikely scenario */
 49644  		return rc;
 49645  	}
 49646  	/* Lookup for the cell */
 49647  	pCell = lhFindCell(pPage,pKey,nByte,nHash);
 49648  	if( pCell == 0 ){
 49649  		/* No such entry */
 49650  		return UNQLITE_NOTFOUND;
 49651  	}
 49652  	if( ppCell ){
 49653  		*ppCell = pCell;
 49654  	}
 49655  	return UNQLITE_OK;
 49656  }
 49657  /*
 49658   * Acquire a new page either from the free list or ask the pager
 49659   * for a new one.
 49660   */
 49661  static int lhAcquirePage(lhash_kv_engine *pEngine,unqlite_page **ppOut)
 49662  {
 49663  	unqlite_page *pPage;
 49664  	int rc;
 49665  	if( pEngine->nFreeList != 0 ){
 49666  		/* Acquire one from the free list */
 49667  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pEngine->nFreeList,&pPage);
 49668  		if( rc == UNQLITE_OK ){
 49669  			/* Point to the next free page */
 49670  			SyBigEndianUnpack64(pPage->zData,&pEngine->nFreeList);
 49671  			/* Update the database header */
 49672  			rc = pEngine->pIo->xWrite(pEngine->pHeader);
 49673  			if( rc != UNQLITE_OK ){
 49674  				return rc;
 49675  			}
 49676  			SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
 49677  			/* Tell the pager do not journal this page */
 49678  			pEngine->pIo->xDontJournal(pPage);
 49679  			/* Return to the caller */
 49680  			*ppOut = pPage;
 49681  			/* All done */
 49682  			return UNQLITE_OK;
 49683  		}
 49684  	}
 49685  	/* Acquire a new page */
 49686  	rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pPage);
 49687  	if( rc != UNQLITE_OK ){
 49688  		return rc;
 49689  	}
 49690  	/* Point to the target page */
 49691  	*ppOut = pPage;
 49692  	return UNQLITE_OK;
 49693  }
 49694  /*
 49695   * Write a bucket map record to disk.
 49696   */
 49697  static int lhMapWriteRecord(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
 49698  {
 49699  	lhash_bmap_page *pMap = &pEngine->sPageMap;
 49700  	unqlite_page *pPage = 0;
 49701  	int rc;
 49702  	if( pMap->iPtr > (pEngine->iPageSize - 16) /* 8 byte logical bucket number + 8 byte real bucket number */ ){
 49703  		unqlite_page *pOld;
 49704  		/* Point to the old page */
 49705  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pOld);
 49706  		if( rc != UNQLITE_OK ){
 49707  			return rc;
 49708  		}
 49709  		/* Acquire a new page */
 49710  		rc = lhAcquirePage(pEngine,&pPage);
 49711  		if( rc != UNQLITE_OK ){
 49712  			return rc;
 49713  		}
 49714  		/* Reflect the change  */
 49715  		pMap->iNext = 0;
 49716  		pMap->iNum = pPage->pgno;
 49717  		pMap->nRec = 0;
 49718  		pMap->iPtr = 8/* Next page number */+4/* Total records in the map*/;
 49719  		/* Link this page */
 49720  		rc = pEngine->pIo->xWrite(pOld);
 49721  		if( rc != UNQLITE_OK ){
 49722  			return rc;
 49723  		}
 49724  		if( pOld->pgno == pEngine->pHeader->pgno ){
 49725  			/* First page (Hash header) */
 49726  			SyBigEndianPack64(&pOld->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/],pPage->pgno);
 49727  		}else{
 49728  			/* Link the new page */
 49729  			SyBigEndianPack64(pOld->zData,pPage->pgno);
 49730  			/* Unref */
 49731  			pEngine->pIo->xPageUnref(pOld);
 49732  		}
 49733  		/* Assume the last bucket map page */
 49734  		rc = pEngine->pIo->xWrite(pPage);
 49735  		if( rc != UNQLITE_OK ){
 49736  			return rc;
 49737  		}
 49738  		SyBigEndianPack64(pPage->zData,0); /* Next bucket map page on the list */
 49739  	}
 49740  	if( pPage == 0){
 49741  		/* Point to the current map page */
 49742  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pPage);
 49743  		if( rc != UNQLITE_OK ){
 49744  			return rc;
 49745  		}
 49746  	}
 49747  	/* Make page writable */
 49748  	rc = pEngine->pIo->xWrite(pPage);
 49749  	if( rc != UNQLITE_OK ){
 49750  		return rc;
 49751  	}
 49752  	/* Write the data */
 49753  	SyBigEndianPack64(&pPage->zData[pMap->iPtr],iLogic);
 49754  	pMap->iPtr += 8;
 49755  	SyBigEndianPack64(&pPage->zData[pMap->iPtr],iReal);
 49756  	pMap->iPtr += 8;
 49757  	/* Install the bucket map */
 49758  	rc = lhMapInstallBucket(pEngine,iLogic,iReal);
 49759  	if( rc == UNQLITE_OK ){
 49760  		/* Total number of records */
 49761  		pMap->nRec++;
 49762  		if( pPage->pgno == pEngine->pHeader->pgno ){
 49763  			/* Page one: Always writable */
 49764  			SyBigEndianPack32(
 49765  				&pPage->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/+8/*Next map page*/],
 49766  				pMap->nRec);
 49767  		}else{
 49768  			/* Make page writable */
 49769  			rc = pEngine->pIo->xWrite(pPage);
 49770  			if( rc != UNQLITE_OK ){
 49771  				return rc;
 49772  			}
 49773  			SyBigEndianPack32(&pPage->zData[8],pMap->nRec);
 49774  		}
 49775  	}
 49776  	return rc;
 49777  }
 49778  /*
 49779   * Defragment a page.
 49780   */
 49781  static int lhPageDefragment(lhpage *pPage)
 49782  {
 49783  	lhash_kv_engine *pEngine = pPage->pHash;
 49784  	unsigned char *zTmp,*zPtr,*zEnd,*zPayload;
 49785  	lhcell *pCell;
 49786  	/* Get a temporary page from the pager. This opertaion never fail */
 49787  	zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle);
 49788  	/* Move the target cells to the begining */
 49789  	pCell = pPage->pList;
 49790  	/* Write the slave page number */
 49791  	SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave);
 49792  	zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */
 49793  	zEnd = &zTmp[pEngine->iPageSize];
 49794  	pPage->sHdr.iOfft = 0; /* Offset of the first cell */
 49795  	for(;;){
 49796  		if( pCell == 0 ){
 49797  			/* No more cells */
 49798  			break;
 49799  		}
 49800  		if( pCell->pPage->pRaw->pgno == pPage->pRaw->pgno ){
 49801  			/* Cell payload if locally stored */
 49802  			zPayload = 0;
 49803  			if( pCell->iOvfl == 0 ){
 49804  				zPayload = &pCell->pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ];
 49805  			}
 49806  			/* Move the cell */
 49807  			pCell->iNext = pPage->sHdr.iOfft;
 49808  			pCell->iStart = (sxu16)(zPtr - zTmp); /* Offset where this cell start */
 49809  			pPage->sHdr.iOfft = pCell->iStart;
 49810  			/* Write the cell header */
 49811  			/* 4 byte hash number */
 49812  			SyBigEndianPack32(zPtr,pCell->nHash);
 49813  			zPtr += 4;
 49814  			/* 4 byte ley length */
 49815  			SyBigEndianPack32(zPtr,pCell->nKey);
 49816  			zPtr += 4;
 49817  			/* 8 byte data length */
 49818  			SyBigEndianPack64(zPtr,pCell->nData);
 49819  			zPtr += 8;
 49820  			/* 2 byte offset of the next cell */
 49821  			SyBigEndianPack16(zPtr,pCell->iNext);
 49822  			zPtr += 2;
 49823  			/* 8 byte overflow page number */
 49824  			SyBigEndianPack64(zPtr,pCell->iOvfl);
 49825  			zPtr += 8;
 49826  			if( zPayload ){
 49827  				/* Local payload */
 49828  				SyMemcpy((const void *)zPayload,zPtr,(sxu32)(pCell->nKey + pCell->nData));
 49829  				zPtr += pCell->nKey + pCell->nData;
 49830  			}
 49831  			if( zPtr >= zEnd ){
 49832  				/* Can't happen */
 49833  				break;
 49834  			}
 49835  		}
 49836  		/* Point to the next page */
 49837  		pCell = pCell->pNext;
 49838  	}
 49839  	/* Mark the free block */
 49840  	pPage->nFree = (sxu16)(zEnd - zPtr); /* Block length */
 49841  	if( pPage->nFree > 3 ){
 49842  		pPage->sHdr.iFree = (sxu16)(zPtr - zTmp); /* Offset of the free block */
 49843  		/* Mark the block */
 49844  		SyBigEndianPack16(zPtr,0); /* Offset of the next free block */
 49845  		SyBigEndianPack16(&zPtr[2],pPage->nFree); /* Block length */
 49846  	}else{
 49847  		/* Block of length less than 4 bytes are simply discarded */
 49848  		pPage->nFree = 0;
 49849  		pPage->sHdr.iFree = 0;
 49850  	}
 49851  	/* Reflect the change */
 49852  	SyBigEndianPack16(zTmp,pPage->sHdr.iOfft);     /* Offset of the first cell */
 49853  	SyBigEndianPack16(&zTmp[2],pPage->sHdr.iFree); /* Offset of the first free block */
 49854  	SyMemcpy((const void *)zTmp,pPage->pRaw->zData,pEngine->iPageSize);
 49855  	/* All done */
 49856  	return UNQLITE_OK;
 49857  }
 49858  /*
 49859  ** Allocate nByte bytes of space on a page.
 49860  **
 49861  ** Return the index into pPage->pRaw->zData[] of the first byte of
 49862  ** the new allocation. Or return 0 if there is not enough free
 49863  ** space on the page to satisfy the allocation request.
 49864  **
 49865  ** If the page contains nBytes of free space but does not contain
 49866  ** nBytes of contiguous free space, then this routine automatically
 49867  ** calls defragementPage() to consolidate all free space before 
 49868  ** allocating the new chunk.
 49869  */
 49870  static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft)
 49871  {
 49872  	const unsigned char *zEnd,*zPtr;
 49873  	sxu16 iNext,iBlksz,nByte;
 49874  	unsigned char *zPrev;
 49875  	int rc;
 49876  	if( (sxu64)pPage->nFree < nAmount ){
 49877  		/* Don't bother looking for a free chunk */
 49878  		return UNQLITE_FULL;
 49879  	}
 49880  	if( pPage->nCell < 10 && ((int)nAmount >= (pPage->pHash->iPageSize / 2)) ){
 49881  		/* Big chunk need an overflow page for its data */
 49882  		return UNQLITE_FULL;
 49883  	}
 49884  	zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
 49885  	zEnd = &pPage->pRaw->zData[pPage->pHash->iPageSize];
 49886  	nByte = (sxu16)nAmount;
 49887  	zPrev = 0;
 49888  	iBlksz = 0; /* cc warning */
 49889  	/* Perform the lookup */
 49890  	for(;;){
 49891  		if( zPtr >= zEnd ){
 49892  			return UNQLITE_FULL;
 49893  		}
 49894  		/* Offset of the next free block */
 49895  		SyBigEndianUnpack16(zPtr,&iNext);
 49896  		/* Block size */
 49897  		SyBigEndianUnpack16(&zPtr[2],&iBlksz);
 49898  		if( iBlksz >= nByte ){
 49899  			/* Got one */
 49900  			break;
 49901  		}
 49902  		zPrev = (unsigned char *)zPtr;
 49903  		if( iNext == 0 ){
 49904  			/* No more free blocks, defragment the page */
 49905  			rc = lhPageDefragment(pPage);
 49906  			if( rc == UNQLITE_OK && pPage->nFree >= nByte) {
 49907  				/* Free blocks are merged together */
 49908  				iNext = 0;
 49909  				zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
 49910  				iBlksz = pPage->nFree;
 49911  				zPrev = 0;
 49912  				break;
 49913  			}else{
 49914  				return UNQLITE_FULL;
 49915  			}
 49916  		}
 49917  		/* Point to the next free block */
 49918  		zPtr = &pPage->pRaw->zData[iNext];
 49919  	}
 49920  	/* Acquire writer lock on this page */
 49921  	rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
 49922  	if( rc != UNQLITE_OK ){
 49923  		return rc;
 49924  	}
 49925  	/* Save block offset */
 49926  	*pOfft = (sxu16)(zPtr - pPage->pRaw->zData);
 49927  	/* Fix pointers */
 49928  	if( iBlksz >= nByte && (iBlksz - nByte) > 3 ){
 49929  		unsigned char *zBlock = &pPage->pRaw->zData[(*pOfft) + nByte];
 49930  		/* Create a new block */
 49931  		zPtr = zBlock;
 49932  		SyBigEndianPack16(zBlock,iNext); /* Offset of the next block */
 49933  		SyBigEndianPack16(&zBlock[2],iBlksz-nByte); /* Block size*/
 49934  		/* Offset of the new block */
 49935  		iNext = (sxu16)(zPtr - pPage->pRaw->zData);
 49936  	}
 49937  	/* Fix offsets */
 49938  	if( zPrev ){
 49939  		SyBigEndianPack16(zPrev,iNext);
 49940  	}else{
 49941  		/* First block */
 49942  		pPage->sHdr.iFree = iNext;
 49943  		/* Reflect on the page header */
 49944  		SyBigEndianPack16(&pPage->pRaw->zData[2/* Offset of the first cell1*/],iNext);
 49945  	}
 49946  	/* All done */
 49947  	pPage->nFree -= nByte;
 49948  	return UNQLITE_OK;
 49949  }
 49950  /*
 49951   * Write the cell header into the corresponding offset.
 49952   */
 49953  static int lhCellWriteHeader(lhcell *pCell)
 49954  {
 49955  	lhpage *pPage = pCell->pPage;
 49956  	unsigned char *zRaw = pPage->pRaw->zData;
 49957  	/* Seek to the desired location */
 49958  	zRaw += pCell->iStart;
 49959  	/* 4 byte hash number */
 49960  	SyBigEndianPack32(zRaw,pCell->nHash);
 49961  	zRaw += 4;
 49962  	/* 4 byte key length */
 49963  	SyBigEndianPack32(zRaw,pCell->nKey);
 49964  	zRaw += 4;
 49965  	/* 8 byte data length */
 49966  	SyBigEndianPack64(zRaw,pCell->nData);
 49967  	zRaw += 8;
 49968  	/* 2 byte offset of the next cell */
 49969  	pCell->iNext = pPage->sHdr.iOfft;
 49970  	SyBigEndianPack16(zRaw,pCell->iNext);
 49971  	zRaw += 2;
 49972  	/* 8 byte overflow page number */
 49973  	SyBigEndianPack64(zRaw,pCell->iOvfl);
 49974  	/* Update the page header */
 49975  	pPage->sHdr.iOfft = pCell->iStart;
 49976  	/* pEngine->pIo->xWrite() has been successfully called on this page */
 49977  	SyBigEndianPack16(pPage->pRaw->zData,pCell->iStart);
 49978  	/* All done */
 49979  	return UNQLITE_OK;
 49980  }
 49981  /*
 49982   * Write local payload.
 49983   */
 49984  static int lhCellWriteLocalPayload(lhcell *pCell,
 49985  	const void *pKey,sxu32 nKeylen,
 49986  	const void *pData,unqlite_int64 nDatalen
 49987  	)
 49988  {
 49989  	/* A writer lock have been acquired on this page */
 49990  	lhpage *pPage = pCell->pPage;
 49991  	unsigned char *zRaw = pPage->pRaw->zData;
 49992  	/* Seek to the desired location */
 49993  	zRaw += pCell->iStart + L_HASH_CELL_SZ;
 49994  	/* Write the key */
 49995  	SyMemcpy(pKey,(void *)zRaw,nKeylen);
 49996  	zRaw += nKeylen;
 49997  	if( nDatalen > 0 ){
 49998  		/* Write the Data */
 49999  		SyMemcpy(pData,(void *)zRaw,(sxu32)nDatalen);
 50000  	}
 50001  	return UNQLITE_OK;
 50002  }
 50003  /*
 50004   * Allocate as much overflow page we need to store the cell payload.
 50005   */
 50006  static int lhCellWriteOvflPayload(lhcell *pCell,const void *pKey,sxu32 nKeylen,...)
 50007  {
 50008  	lhpage *pPage = pCell->pPage;
 50009  	lhash_kv_engine *pEngine = pPage->pHash;
 50010  	unqlite_page *pOvfl,*pFirst,*pNew;
 50011  	const unsigned char *zPtr,*zEnd;
 50012  	unsigned char *zRaw,*zRawEnd;
 50013  	sxu32 nAvail;
 50014  	va_list ap;
 50015  	int rc;
 50016  	/* Acquire a new overflow page */
 50017  	rc = lhAcquirePage(pEngine,&pOvfl);
 50018  	if( rc != UNQLITE_OK ){
 50019  		return rc;
 50020  	}
 50021  	/* Acquire a writer lock */
 50022  	rc = pEngine->pIo->xWrite(pOvfl);
 50023  	if( rc != UNQLITE_OK ){
 50024  		return rc;
 50025  	}
 50026  	pFirst = pOvfl;
 50027  	/* Link */
 50028  	pCell->iOvfl = pOvfl->pgno;
 50029  	/* Update the cell header */
 50030  	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4/*Hash*/ + 4/*Key*/ + 8/*Data*/ + 2 /*Next cell*/],pCell->iOvfl);
 50031  	/* Start the write process */
 50032  	zPtr = (const unsigned char *)pKey;
 50033  	zEnd = &zPtr[nKeylen];
 50034  	SyBigEndianPack64(pOvfl->zData,0); /* Next overflow page on the chain */
 50035  	zRaw = &pOvfl->zData[8/* Next ovfl page*/ + 8 /* Data page */ + 2 /* Data offset*/];
 50036  	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
 50037  	pNew = pOvfl;
 50038  	/* Write the key */
 50039  	for(;;){
 50040  		if( zPtr >= zEnd ){
 50041  			break;
 50042  		}
 50043  		if( zRaw >= zRawEnd ){
 50044  			/* Acquire a new page */
 50045  			rc = lhAcquirePage(pEngine,&pNew);
 50046  			if( rc != UNQLITE_OK ){
 50047  				return rc;
 50048  			}
 50049  			rc = pEngine->pIo->xWrite(pNew);
 50050  			if( rc != UNQLITE_OK ){
 50051  				return rc;
 50052  			}
 50053  			/* Link */
 50054  			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
 50055  			pEngine->pIo->xPageUnref(pOvfl);
 50056  			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
 50057  			pOvfl = pNew;
 50058  			zRaw = &pNew->zData[8];
 50059  			zRawEnd = &pNew->zData[pEngine->iPageSize];
 50060  		}
 50061  		nAvail = (sxu32)(zRawEnd-zRaw);
 50062  		nKeylen = (sxu32)(zEnd-zPtr);
 50063  		if( nKeylen > nAvail ){
 50064  			nKeylen = nAvail;
 50065  		}
 50066  		SyMemcpy((const void *)zPtr,(void *)zRaw,nKeylen);
 50067  		/* Synchronize pointers */
 50068  		zPtr += nKeylen;
 50069  		zRaw += nKeylen;
 50070  	}
 50071  	rc = UNQLITE_OK;
 50072  	va_start(ap,nKeylen);
 50073  	pCell->iDataPage = pNew->pgno;
 50074  	pCell->iDataOfft = (sxu16)(zRaw-pNew->zData);
 50075  	/* Write the data page and its offset */
 50076  	SyBigEndianPack64(&pFirst->zData[8/*Next ovfl*/],pCell->iDataPage);
 50077  	SyBigEndianPack16(&pFirst->zData[8/*Next ovfl*/+8/*Data page*/],pCell->iDataOfft);
 50078  	/* Write data */
 50079  	for(;;){
 50080  		const void *pData;
 50081  		sxu32 nDatalen;
 50082  		sxu64 nData;
 50083  		pData = va_arg(ap,const void *);
 50084  		nData = va_arg(ap,sxu64);
 50085  		if( pData == 0 ){
 50086  			/* No more chunks */
 50087  			break;
 50088  		}
 50089  		/* Write this chunk */
 50090  		zPtr = (const unsigned char *)pData;
 50091  		zEnd = &zPtr[nData];
 50092  		for(;;){
 50093  			if( zPtr >= zEnd ){
 50094  				break;
 50095  			}
 50096  			if( zRaw >= zRawEnd ){
 50097  				/* Acquire a new page */
 50098  				rc = lhAcquirePage(pEngine,&pNew);
 50099  				if( rc != UNQLITE_OK ){
 50100  					va_end(ap);
 50101  					return rc;
 50102  				}
 50103  				rc = pEngine->pIo->xWrite(pNew);
 50104  				if( rc != UNQLITE_OK ){
 50105  					va_end(ap);
 50106  					return rc;
 50107  				}
 50108  				/* Link */
 50109  				SyBigEndianPack64(pOvfl->zData,pNew->pgno);
 50110  				pEngine->pIo->xPageUnref(pOvfl);
 50111  				SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
 50112  				pOvfl = pNew;
 50113  				zRaw = &pNew->zData[8];
 50114  				zRawEnd = &pNew->zData[pEngine->iPageSize];
 50115  			}
 50116  			nAvail = (sxu32)(zRawEnd-zRaw);
 50117  			nDatalen = (sxu32)(zEnd-zPtr);
 50118  			if( nDatalen > nAvail ){
 50119  				nDatalen = nAvail;
 50120  			}
 50121  			SyMemcpy((const void *)zPtr,(void *)zRaw,nDatalen);
 50122  			/* Synchronize pointers */
 50123  			zPtr += nDatalen;
 50124  			zRaw += nDatalen;
 50125  		}
 50126  	}
 50127  	/* Unref the overflow page */
 50128  	pEngine->pIo->xPageUnref(pOvfl);
 50129  	va_end(ap);
 50130  	return UNQLITE_OK;
 50131  }
 50132  /*
 50133   * Restore a page to the free list.
 50134   */
 50135  static int lhRestorePage(lhash_kv_engine *pEngine,unqlite_page *pPage)
 50136  {
 50137  	int rc;
 50138  	rc = pEngine->pIo->xWrite(pEngine->pHeader);
 50139  	if( rc != UNQLITE_OK ){
 50140  		return rc;
 50141  	}
 50142  	rc = pEngine->pIo->xWrite(pPage);
 50143  	if( rc != UNQLITE_OK ){
 50144  		return rc;
 50145  	}
 50146  	/* Link to the list of free page */
 50147  	SyBigEndianPack64(pPage->zData,pEngine->nFreeList);
 50148  	pEngine->nFreeList = pPage->pgno;
 50149  	SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
 50150  	/* All done */
 50151  	return UNQLITE_OK;
 50152  }
 50153  /*
 50154   * Restore cell space and mark it as a free block.
 50155   */
 50156  static int lhRestoreSpace(lhpage *pPage,sxu16 iOfft,sxu16 nByte)
 50157  {
 50158  	unsigned char *zRaw;
 50159  	if( nByte < 4 ){
 50160  		/* At least 4 bytes of freespace must form a valid block */
 50161  		return UNQLITE_OK;
 50162  	}
 50163  	/* pEngine->pIo->xWrite() has been successfully called on this page */
 50164  	zRaw = &pPage->pRaw->zData[iOfft];
 50165  	/* Mark as a free block */
 50166  	SyBigEndianPack16(zRaw,pPage->sHdr.iFree); /* Offset of the next free block */
 50167  	zRaw += 2;
 50168  	SyBigEndianPack16(zRaw,nByte);
 50169  	/* Link */
 50170  	SyBigEndianPack16(&pPage->pRaw->zData[2/* offset of the first cell */],iOfft);
 50171  	pPage->sHdr.iFree = iOfft;
 50172  	pPage->nFree += nByte;
 50173  	return UNQLITE_OK;
 50174  }
 50175  /* Forward declaration */
 50176  static lhcell * lhFindSibeling(lhcell *pCell);
 50177  /*
 50178   * Unlink a cell.
 50179   */
 50180  static int lhUnlinkCell(lhcell *pCell)
 50181  {
 50182  	lhash_kv_engine *pEngine = pCell->pPage->pHash;
 50183  	lhpage *pPage = pCell->pPage;
 50184  	sxu16 nByte = L_HASH_CELL_SZ;
 50185  	lhcell *pPrev;
 50186  	int rc;
 50187  	rc = pEngine->pIo->xWrite(pPage->pRaw);
 50188  	if( rc != UNQLITE_OK ){
 50189  		return rc;
 50190  	}
 50191  	/* Bring the link */
 50192  	pPrev = lhFindSibeling(pCell);
 50193  	if( pPrev ){
 50194  		pPrev->iNext = pCell->iNext;
 50195  		/* Fix offsets in the page header */
 50196  		SyBigEndianPack16(&pPage->pRaw->zData[pPrev->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
 50197  	}else{
 50198  		/* First entry on this page (either master or slave) */
 50199  		pPage->sHdr.iOfft = pCell->iNext;
 50200  		/* Update the page header */
 50201  		SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
 50202  	}
 50203  	/* Restore cell space */
 50204  	if( pCell->iOvfl == 0 ){
 50205  		nByte += (sxu16)(pCell->nData + pCell->nKey);
 50206  	}
 50207  	lhRestoreSpace(pPage,pCell->iStart,nByte);
 50208  	/* Discard the cell from the in-memory hashtable */
 50209  	lhCellDiscard(pCell);
 50210  	return UNQLITE_OK;
 50211  }
 50212  /*
 50213   * Remove a cell and its paylod (key + data).
 50214   */
 50215  static int lhRecordRemove(lhcell *pCell)
 50216  {
 50217  	lhash_kv_engine *pEngine = pCell->pPage->pHash;
 50218  	int rc;
 50219  	if( pCell->iOvfl > 0){
 50220  		/* Discard overflow pages */
 50221  		unqlite_page *pOvfl;
 50222  		pgno iNext = pCell->iOvfl;
 50223  		for(;;){
 50224  			/* Point to the overflow page */
 50225  			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pOvfl);
 50226  			if( rc != UNQLITE_OK ){
 50227  				return rc;
 50228  			}
 50229  			/* Next page on the chain */
 50230  			SyBigEndianUnpack64(pOvfl->zData,&iNext);
 50231  			/* Restore the page to the free list */
 50232  			rc = lhRestorePage(pEngine,pOvfl);
 50233  			if( rc != UNQLITE_OK ){
 50234  				return rc;
 50235  			}
 50236  			/* Unref */
 50237  			pEngine->pIo->xPageUnref(pOvfl);
 50238  			if( iNext == 0 ){
 50239  				break;
 50240  			}
 50241  		}
 50242  	}
 50243  	/* Unlink the cell */
 50244  	rc = lhUnlinkCell(pCell);
 50245  	return rc;
 50246  }
 50247  /*
 50248   * Find cell sibeling.
 50249   */
 50250  static lhcell * lhFindSibeling(lhcell *pCell)
 50251  {
 50252  	lhpage *pPage = pCell->pPage->pMaster;
 50253  	lhcell *pEntry;
 50254  	pEntry = pPage->pFirst; 
 50255  	while( pEntry ){
 50256  		if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){
 50257  			/* Sibeling found */
 50258  			return pEntry;
 50259  		}
 50260  		/* Point to the previous entry */
 50261  		pEntry = pEntry->pPrev; 
 50262  	}
 50263  	/* Last inserted cell */
 50264  	return 0;
 50265  }
 50266  /*
 50267   * Move a cell to a new location with its new data.
 50268   */
 50269  static int lhMoveLocalCell(
 50270  	lhcell *pCell,
 50271  	sxu16 iOfft,
 50272  	const void *pData,
 50273  	unqlite_int64 nData
 50274  	)
 50275  {
 50276  	sxu16 iKeyOfft = pCell->iStart + L_HASH_CELL_SZ;
 50277  	lhpage *pPage = pCell->pPage;
 50278  	lhcell *pSibeling;
 50279  	pSibeling = lhFindSibeling(pCell);
 50280  	if( pSibeling ){
 50281  		/* Fix link */
 50282  		SyBigEndianPack16(&pPage->pRaw->zData[pSibeling->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
 50283  		pSibeling->iNext = pCell->iNext;
 50284  	}else{
 50285  		/* First cell, update page header only */
 50286  		SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
 50287  		pPage->sHdr.iOfft = pCell->iNext;
 50288  	}
 50289  	/* Set the new offset */
 50290  	pCell->iStart = iOfft;
 50291  	pCell->nData = (sxu64)nData;
 50292  	/* Write the cell payload */
 50293  	lhCellWriteLocalPayload(pCell,(const void *)&pPage->pRaw->zData[iKeyOfft],pCell->nKey,pData,nData);
 50294  	/* Finally write the cell header */
 50295  	lhCellWriteHeader(pCell);
 50296  	/* All done */
 50297  	return UNQLITE_OK;
 50298  }
 50299  /*
 50300   * Overwrite an existing record.
 50301   */
 50302  static int lhRecordOverwrite(
 50303  	lhcell *pCell,
 50304  	const void *pData,unqlite_int64 nByte
 50305  	)
 50306  {
 50307  	lhash_kv_engine *pEngine = pCell->pPage->pHash;
 50308  	unsigned char *zRaw,*zRawEnd,*zPayload;
 50309  	const unsigned char *zPtr,*zEnd;
 50310  	unqlite_page *pOvfl,*pOld,*pNew;
 50311  	lhpage *pPage = pCell->pPage;
 50312  	sxu32 nAvail;
 50313  	pgno iOvfl;
 50314  	int rc;
 50315  	/* Acquire a writer lock on this page */
 50316  	rc = pEngine->pIo->xWrite(pPage->pRaw);
 50317  	if( rc != UNQLITE_OK ){
 50318  		return rc;
 50319  	}
 50320  	if( pCell->iOvfl == 0 ){
 50321  		/* Local payload, try to deal with the free space issues */
 50322  		zPayload = &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey];
 50323  		if( pCell->nData == (sxu64)nByte ){
 50324  			/* Best scenario, simply a memcpy operation */
 50325  			SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
 50326  		}else if( (sxu64)nByte < pCell->nData ){
 50327  			/* Shorter data, not so ugly */
 50328  			SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
 50329  			/* Update the cell header */
 50330  			SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],nByte);
 50331  			/* Restore freespace */
 50332  			lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ + pCell->nKey + nByte),(sxu16)(pCell->nData - nByte));
 50333  			/* New data size */
 50334  			pCell->nData = (sxu64)nByte;
 50335  		}else{
 50336  			sxu16 iOfft = 0; /* cc warning */
 50337  			/* Check if another chunk is available for this cell */
 50338  			rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + nByte,&iOfft);
 50339  			if( rc != UNQLITE_OK ){
 50340  				/* Transfer the payload to an overflow page */
 50341  				rc = lhCellWriteOvflPayload(pCell,&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,pData,nByte,(const void *)0);
 50342  				if( rc != UNQLITE_OK ){
 50343  					return rc;
 50344  				}
 50345  				/* Update the cell header */
 50346  				SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],(sxu64)nByte);
 50347  				/* Restore freespace */
 50348  				lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
 50349  				/* New data size */
 50350  				pCell->nData = (sxu64)nByte;
 50351  			}else{
 50352  				sxu16 iOldOfft = pCell->iStart;
 50353  				sxu32 iOld = (sxu32)pCell->nData;
 50354  				/* Space is available, transfer the cell */
 50355  				lhMoveLocalCell(pCell,iOfft,pData,nByte);
 50356  				/* Restore cell space */
 50357  				lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
 50358  			}
 50359  		}
 50360  		return UNQLITE_OK;
 50361  	}
 50362  	/* Point to the overflow page */
 50363  	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
 50364  	if( rc != UNQLITE_OK ){
 50365  		return rc;
 50366  	}
 50367  	/* Relase all old overflow pages first */
 50368  	SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
 50369  	pOld = pOvfl;
 50370  	for(;;){
 50371  		if( iOvfl == 0 ){
 50372  			/* No more overflow pages on the chain */
 50373  			break;
 50374  		}
 50375  		/* Point to the target page */
 50376  		if( UNQLITE_OK != pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOld) ){
 50377  			/* Not so fatal if something goes wrong here */
 50378  			break;
 50379  		}
 50380  		/* Next overflow page to be released */
 50381  		SyBigEndianUnpack64(pOld->zData,&iOvfl);
 50382  		if( pOld != pOvfl ){ /* xx: chm is maniac */
 50383  			/* Restore the page to the free list */
 50384  			lhRestorePage(pEngine,pOld);
 50385  			/* Unref */
 50386  			pEngine->pIo->xPageUnref(pOld);
 50387  		}
 50388  	}
 50389  	/* Point to the data offset */
 50390  	zRaw = &pOvfl->zData[pCell->iDataOfft];
 50391  	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
 50392  	/* The data to be stored */
 50393  	zPtr = (const unsigned char *)pData;
 50394  	zEnd = &zPtr[nByte];
 50395  	/* Start the overwrite process */
 50396  	/* Acquire a writer lock */
 50397  	rc = pEngine->pIo->xWrite(pOvfl);
 50398  	if( rc != UNQLITE_OK ){
 50399  		return rc;
 50400  	}
 50401  	SyBigEndianPack64(pOvfl->zData,0);
 50402  	for(;;){
 50403  		sxu32 nLen;
 50404  		if( zPtr >= zEnd ){
 50405  			break;
 50406  		}
 50407  		if( zRaw >= zRawEnd ){
 50408  			/* Acquire a new page */
 50409  			rc = lhAcquirePage(pEngine,&pNew);
 50410  			if( rc != UNQLITE_OK ){
 50411  				return rc;
 50412  			}
 50413  			rc = pEngine->pIo->xWrite(pNew);
 50414  			if( rc != UNQLITE_OK ){
 50415  				return rc;
 50416  			}
 50417  			/* Link */
 50418  			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
 50419  			pEngine->pIo->xPageUnref(pOvfl);
 50420  			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
 50421  			pOvfl = pNew;
 50422  			zRaw = &pNew->zData[8];
 50423  			zRawEnd = &pNew->zData[pEngine->iPageSize];
 50424  		}
 50425  		nAvail = (sxu32)(zRawEnd-zRaw);
 50426  		nLen = (sxu32)(zEnd-zPtr);
 50427  		if( nLen > nAvail ){
 50428  			nLen = nAvail;
 50429  		}
 50430  		SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
 50431  		/* Synchronize pointers */
 50432  		zPtr += nLen;
 50433  		zRaw += nLen;
 50434  	}
 50435  	/* Unref the last overflow page */
 50436  	pEngine->pIo->xPageUnref(pOvfl);
 50437  	/* Finally, update the cell header */
 50438  	pCell->nData = (sxu64)nByte;
 50439  	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
 50440  	/* All done */
 50441  	return UNQLITE_OK;
 50442  }
 50443  /*
 50444   * Append data to an existing record.
 50445   */
 50446  static int lhRecordAppend(
 50447  	lhcell *pCell,
 50448  	const void *pData,unqlite_int64 nByte
 50449  	)
 50450  {
 50451  	lhash_kv_engine *pEngine = pCell->pPage->pHash;
 50452  	const unsigned char *zPtr,*zEnd;
 50453  	lhpage *pPage = pCell->pPage;
 50454  	unsigned char *zRaw,*zRawEnd;
 50455  	unqlite_page *pOvfl,*pNew;
 50456  	sxu64 nDatalen;
 50457  	sxu32 nAvail;
 50458  	pgno iOvfl;
 50459  	int rc;
 50460  	if( pCell->nData + nByte < pCell->nData ){
 50461  		/* Overflow */
 50462  		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
 50463  		return UNQLITE_LIMIT;
 50464  	}
 50465  	/* Acquire a writer lock on this page */
 50466  	rc = pEngine->pIo->xWrite(pPage->pRaw);
 50467  	if( rc != UNQLITE_OK ){
 50468  		return rc;
 50469  	}
 50470  	if( pCell->iOvfl == 0 ){
 50471  		sxu16 iOfft = 0; /* cc warning */
 50472  		/* Local payload, check for a bigger place */
 50473  		rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + pCell->nData + nByte,&iOfft);
 50474  		if( rc != UNQLITE_OK ){
 50475  			/* Transfer the payload to an overflow page */
 50476  			rc = lhCellWriteOvflPayload(pCell,
 50477  				&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,
 50478  				(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],pCell->nData,
 50479  				pData,nByte,
 50480  				(const void *)0);
 50481  			if( rc != UNQLITE_OK ){
 50482  				return rc;
 50483  			}
 50484  			/* Update the cell header */
 50485  			SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData + nByte);
 50486  			/* Restore freespace */
 50487  			lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
 50488  			/* New data size */
 50489  			pCell->nData += nByte;
 50490  		}else{
 50491  			sxu16 iOldOfft = pCell->iStart;
 50492  			sxu32 iOld = (sxu32)pCell->nData;
 50493  			SyBlob sWorker;
 50494  			SyBlobInit(&sWorker,&pEngine->sAllocator);
 50495  			/* Copy the old data */
 50496  			rc = SyBlobAppend(&sWorker,(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],(sxu32)pCell->nData);
 50497  			if( rc == SXRET_OK ){
 50498  				/* Append the new data */
 50499  				rc = SyBlobAppend(&sWorker,pData,(sxu32)nByte);
 50500  			}
 50501  			if( rc != UNQLITE_OK ){
 50502  				SyBlobRelease(&sWorker);
 50503  				return rc;
 50504  			}
 50505  			/* Space is available, transfer the cell */
 50506  			lhMoveLocalCell(pCell,iOfft,SyBlobData(&sWorker),(unqlite_int64)SyBlobLength(&sWorker));
 50507  			/* Restore cell space */
 50508  			lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
 50509  			/* All done */
 50510  			SyBlobRelease(&sWorker);
 50511  		}
 50512  		return UNQLITE_OK;
 50513  	}
 50514  	/* Point to the overflow page which hold the data */
 50515  	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
 50516  	if( rc != UNQLITE_OK ){
 50517  		return rc;
 50518  	}
 50519  	/* Next overflow page in the chain */
 50520  	SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
 50521  	/* Point to the end of the chunk */
 50522  	zRaw = &pOvfl->zData[pCell->iDataOfft];
 50523  	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
 50524  	nDatalen = pCell->nData;
 50525  	nAvail = (sxu32)(zRawEnd - zRaw);
 50526  	for(;;){
 50527  		if( zRaw >= zRawEnd ){
 50528  			if( iOvfl == 0 ){
 50529  				/* Cant happen */
 50530  				pEngine->pIo->xErr(pEngine->pIo->pHandle,"Corrupt overflow page");
 50531  				return UNQLITE_CORRUPT;
 50532  			}
 50533  			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pNew);
 50534  			if( rc != UNQLITE_OK ){
 50535  				return rc;
 50536  			}
 50537  			/* Next overflow page on the chain */
 50538  			SyBigEndianUnpack64(pNew->zData,&iOvfl);
 50539  			/* Unref the previous overflow page */
 50540  			pEngine->pIo->xPageUnref(pOvfl);
 50541  			/* Point to the new chunk */
 50542  			zRaw = &pNew->zData[8];
 50543  			zRawEnd = &pNew->zData[pCell->pPage->pHash->iPageSize];
 50544  			nAvail = L_HASH_OVERFLOW_SIZE(pCell->pPage->pHash->iPageSize);
 50545  			pOvfl = pNew;
 50546  		}
 50547  		if( (sxu64)nAvail > nDatalen ){
 50548  			zRaw += nDatalen;
 50549  			break;
 50550  		}else{
 50551  			nDatalen -= nAvail;
 50552  		}
 50553  		zRaw += nAvail;
 50554  	}
 50555  	/* Start the append process */
 50556  	zPtr = (const unsigned char *)pData;
 50557  	zEnd = &zPtr[nByte];
 50558  	/* Acquire a writer lock */
 50559  	rc = pEngine->pIo->xWrite(pOvfl);
 50560  	if( rc != UNQLITE_OK ){
 50561  		return rc;
 50562  	}
 50563  	for(;;){
 50564  		sxu32 nLen;
 50565  		if( zPtr >= zEnd ){
 50566  			break;
 50567  		}
 50568  		if( zRaw >= zRawEnd ){
 50569  			/* Acquire a new page */
 50570  			rc = lhAcquirePage(pEngine,&pNew);
 50571  			if( rc != UNQLITE_OK ){
 50572  				return rc;
 50573  			}
 50574  			rc = pEngine->pIo->xWrite(pNew);
 50575  			if( rc != UNQLITE_OK ){
 50576  				return rc;
 50577  			}
 50578  			/* Link */
 50579  			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
 50580  			pEngine->pIo->xPageUnref(pOvfl);
 50581  			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
 50582  			pOvfl = pNew;
 50583  			zRaw = &pNew->zData[8];
 50584  			zRawEnd = &pNew->zData[pEngine->iPageSize];
 50585  		}
 50586  		nAvail = (sxu32)(zRawEnd-zRaw);
 50587  		nLen = (sxu32)(zEnd-zPtr);
 50588  		if( nLen > nAvail ){
 50589  			nLen = nAvail;
 50590  		}
 50591  		SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
 50592  		/* Synchronize pointers */
 50593  		zPtr += nLen;
 50594  		zRaw += nLen;
 50595  	}
 50596  	/* Unref the last overflow page */
 50597  	pEngine->pIo->xPageUnref(pOvfl);
 50598  	/* Finally, update the cell header */
 50599  	pCell->nData += nByte;
 50600  	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
 50601  	/* All done */
 50602  	return UNQLITE_OK;
 50603  }
 50604  /*
 50605   * A write privilege have been acquired on this page.
 50606   * Mark it as an empty page (No cells).
 50607   */
 50608  static int lhSetEmptyPage(lhpage *pPage)
 50609  {
 50610  	unsigned char *zRaw = pPage->pRaw->zData;
 50611  	lhphdr *pHeader = &pPage->sHdr;
 50612  	sxu16 nByte;
 50613  	int rc;
 50614  	/* Acquire a writer lock */
 50615  	rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
 50616  	if( rc != UNQLITE_OK ){
 50617  		return rc;
 50618  	}
 50619  	/* Offset of the first cell */
 50620  	SyBigEndianPack16(zRaw,0);
 50621  	zRaw += 2;
 50622  	/* Offset of the first free block */
 50623  	pHeader->iFree = L_HASH_PAGE_HDR_SZ;
 50624  	SyBigEndianPack16(zRaw,L_HASH_PAGE_HDR_SZ);
 50625  	zRaw += 2;
 50626  	/* Slave page number */
 50627  	SyBigEndianPack64(zRaw,0);
 50628  	zRaw += 8;
 50629  	/* Fill the free block */
 50630  	SyBigEndianPack16(zRaw,0); /* Offset of the next free block */
 50631  	zRaw += 2;
 50632  	nByte = (sxu16)L_HASH_MX_FREE_SPACE(pPage->pHash->iPageSize);
 50633  	SyBigEndianPack16(zRaw,nByte);
 50634  	pPage->nFree = nByte;
 50635  	/* Do not add this page to the hot dirty list */
 50636  	pPage->pHash->pIo->xDontMkHot(pPage->pRaw);
 50637  	return UNQLITE_OK;
 50638  }
 50639  /* Forward declaration */
 50640  static int lhSlaveStore(
 50641  	lhpage *pPage,
 50642  	const void *pKey,sxu32 nKeyLen,
 50643  	const void *pData,unqlite_int64 nDataLen,
 50644  	sxu32 nHash
 50645  	);
 50646  /*
 50647   * Store a cell and its payload in a given page.
 50648   */
 50649  static int lhStoreCell(
 50650  	lhpage *pPage, /* Target page */
 50651  	const void *pKey,sxu32 nKeyLen, /* Payload: Key */
 50652  	const void *pData,unqlite_int64 nDataLen, /* Payload: Data */
 50653  	sxu32 nHash, /* Hash of the key */
 50654  	int auto_append /* Auto append a slave page if full */
 50655  	)
 50656  {
 50657  	lhash_kv_engine *pEngine = pPage->pHash;
 50658  	int iNeedOvfl = 0; /* Need overflow page for this cell and its payload*/
 50659  	lhcell *pCell;
 50660  	sxu16 nOfft;
 50661  	int rc;
 50662  	/* Acquire a writer lock on this page first */
 50663  	rc = pEngine->pIo->xWrite(pPage->pRaw);
 50664  	if( rc != UNQLITE_OK ){
 50665  		return rc;
 50666  	}
 50667  	/* Check for a free block  */
 50668  	rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ+nKeyLen+nDataLen,&nOfft);
 50669  	if( rc != UNQLITE_OK ){
 50670  		/* Check for a free block to hold a single cell only (without payload) */
 50671  		rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
 50672  		if( rc != UNQLITE_OK ){
 50673  			if( !auto_append ){
 50674  				/* A split must be done */
 50675  				return UNQLITE_FULL;
 50676  			}else{
 50677  				/* Store this record in a slave page */
 50678  				rc = lhSlaveStore(pPage,pKey,nKeyLen,pData,nDataLen,nHash);
 50679  				return rc;
 50680  			}
 50681  		}
 50682  		iNeedOvfl = 1;
 50683  	}
 50684  	/* Allocate a new cell instance */
 50685  	pCell = lhNewCell(pEngine,pPage);
 50686  	if( pCell == 0 ){
 50687  		pEngine->pIo->xErr(pEngine->pIo->pHandle,"KV store is running out of memory");
 50688  		return UNQLITE_NOMEM;
 50689  	}
 50690  	/* Fill-in the structure */
 50691  	pCell->iStart = nOfft;
 50692  	pCell->nKey = nKeyLen;
 50693  	pCell->nData = (sxu64)nDataLen;
 50694  	pCell->nHash = nHash;
 50695  	if( nKeyLen < 262144 /* 256 KB */ ){
 50696  		/* Keep the key in-memory for fast lookup */
 50697  		SyBlobAppend(&pCell->sKey,pKey,nKeyLen);
 50698  	}
 50699  	/* Link the cell */
 50700  	rc = lhInstallCell(pCell);
 50701  	if( rc != UNQLITE_OK ){
 50702  		return rc;
 50703  	}
 50704  	/* Write the payload */
 50705  	if( iNeedOvfl ){
 50706  		rc = lhCellWriteOvflPayload(pCell,pKey,nKeyLen,pData,nDataLen,(const void *)0);
 50707  		if( rc != UNQLITE_OK ){
 50708  			lhCellDiscard(pCell);
 50709  			return rc;
 50710  		}
 50711  	}else{
 50712  		lhCellWriteLocalPayload(pCell,pKey,nKeyLen,pData,nDataLen);
 50713  	}
 50714  	/* Finally, Write the cell header */
 50715  	lhCellWriteHeader(pCell);
 50716  	/* All done */
 50717  	return UNQLITE_OK;
 50718  }
 50719  /*
 50720   * Find a slave page capable of hosting the given amount.
 50721   */
 50722  static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppSlave)
 50723  {
 50724  	lhash_kv_engine *pEngine = pPage->pHash;
 50725  	lhpage *pMaster = pPage->pMaster;
 50726  	lhpage *pSlave = pMaster->pSlave;
 50727  	unqlite_page *pRaw;
 50728  	lhpage *pNew;
 50729  	sxu16 iOfft;
 50730  	sxi32 i;
 50731  	int rc;
 50732  	/* Look for an already attached slave page */
 50733  	for( i = 0 ; i < pMaster->iSlave ; ++i ){
 50734  		/* Find a free chunk big enough */
 50735  		rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ+nAmount,&iOfft);
 50736  		if( rc != UNQLITE_OK ){
 50737  			/* A space for cell header only */
 50738  			rc = lhAllocateSpace(pSlave,L_HASH_CELL_SZ,&iOfft);
 50739  		}
 50740  		if( rc == UNQLITE_OK ){
 50741  			/* All done */
 50742  			if( pOfft ){
 50743  				*pOfft = iOfft;
 50744  			}
 50745  			*ppSlave = pSlave;
 50746  			return UNQLITE_OK;
 50747  		}
 50748  		/* Point to the next slave page */
 50749  		pSlave = pSlave->pNextSlave;
 50750  	}
 50751  	/* Acquire a new slave page */
 50752  	rc = lhAcquirePage(pEngine,&pRaw);
 50753  	if( rc != UNQLITE_OK ){
 50754  		return rc;
 50755  	}
 50756  	/* Last slave page */
 50757  	pSlave = pMaster->pSlave;
 50758  	if( pSlave == 0 ){
 50759  		/* First slave page */
 50760  		pSlave = pMaster;
 50761  	}
 50762  	/* Initialize the page */
 50763  	pNew = lhNewPage(pEngine,pRaw,pMaster);
 50764  	if( pNew == 0 ){
 50765  		return UNQLITE_NOMEM;
 50766  	}
 50767  	/* Mark as an empty page */
 50768  	rc = lhSetEmptyPage(pNew);
 50769  	if( rc != UNQLITE_OK ){
 50770  		goto fail;
 50771  	}
 50772  	if( pOfft ){
 50773  		/* Look for a free block */
 50774  		if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){
 50775  			/* Cell header only */
 50776  			lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */
 50777  		}	
 50778  		*pOfft = iOfft;
 50779  	}
 50780  	/* Link this page to the previous slave page */
 50781  	rc = pEngine->pIo->xWrite(pSlave->pRaw);
 50782  	if( rc != UNQLITE_OK ){
 50783  		goto fail;
 50784  	}
 50785  	/* Reflect in the page header */
 50786  	SyBigEndianPack64(&pSlave->pRaw->zData[2/*Cell offset*/+2/*Free block offset*/],pRaw->pgno);
 50787  	pSlave->sHdr.iSlave = pRaw->pgno;
 50788  	/* All done */
 50789  	*ppSlave = pNew;
 50790  	return UNQLITE_OK;
 50791  fail:
 50792  	pEngine->pIo->xPageUnref(pNew->pRaw); /* pNew will be released in this call */
 50793  	return rc;
 50794  
 50795  }
 50796  /*
 50797   * Perform a store operation in a slave page.
 50798   */
 50799  static int lhSlaveStore(
 50800  	lhpage *pPage, /* Master page */
 50801  	const void *pKey,sxu32 nKeyLen, /* Payload: key */
 50802  	const void *pData,unqlite_int64 nDataLen, /* Payload: data */
 50803  	sxu32 nHash /* Hash of the key */
 50804  	)
 50805  {
 50806  	lhpage *pSlave;
 50807  	int rc;
 50808  	/* Find a slave page */
 50809  	rc = lhFindSlavePage(pPage,nKeyLen + nDataLen,0,&pSlave);
 50810  	if( rc != UNQLITE_OK ){
 50811  		return rc;
 50812  	}
 50813  	/* Perform the insertion in the slave page */
 50814  	rc = lhStoreCell(pSlave,pKey,nKeyLen,pData,nDataLen,nHash,1);
 50815  	return rc;
 50816  }
 50817  /*
 50818   * Transfer a cell to a new page (either a master or slave).
 50819   */
 50820  static int lhTransferCell(lhcell *pTarget,lhpage *pPage)
 50821  {
 50822  	lhcell *pCell;
 50823  	sxu16 nOfft;
 50824  	int rc;
 50825  	/* Check for a free block to hold a single cell only */
 50826  	rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
 50827  	if( rc != UNQLITE_OK ){
 50828  		/* Store in a slave page */
 50829  		rc = lhFindSlavePage(pPage,L_HASH_CELL_SZ,&nOfft,&pPage);
 50830  		if( rc != UNQLITE_OK ){
 50831  			return rc;
 50832  		}
 50833  	}
 50834  	/* Allocate a new cell instance */
 50835  	pCell = lhNewCell(pPage->pHash,pPage);
 50836  	if( pCell == 0 ){
 50837  		return UNQLITE_NOMEM;
 50838  	}
 50839  	/* Fill-in the structure */
 50840  	pCell->iStart = nOfft;
 50841  	pCell->nData  = pTarget->nData;
 50842  	pCell->nKey   = pTarget->nKey;
 50843  	pCell->iOvfl  = pTarget->iOvfl;
 50844  	pCell->iDataOfft = pTarget->iDataOfft;
 50845  	pCell->iDataPage = pTarget->iDataPage;
 50846  	pCell->nHash = pTarget->nHash;
 50847  	SyBlobDup(&pTarget->sKey,&pCell->sKey);
 50848  	/* Link the cell */
 50849  	rc = lhInstallCell(pCell);
 50850  	if( rc != UNQLITE_OK ){
 50851  		return rc;
 50852  	}
 50853  	/* Finally, Write the cell header */
 50854  	lhCellWriteHeader(pCell);
 50855  	/* All done */
 50856  	return UNQLITE_OK;
 50857  }
 50858  /*
 50859   * Perform a page split.
 50860   */
 50861  static int lhPageSplit(
 50862  	lhpage *pOld,      /* Page to be split */
 50863  	lhpage *pNew,      /* New page */
 50864  	pgno split_bucket, /* Current split bucket */
 50865  	pgno high_mask     /* High mask (Max split bucket - 1) */
 50866  	)
 50867  {
 50868  	lhcell *pCell,*pNext;
 50869  	SyBlob sWorker;
 50870  	pgno iBucket;
 50871  	int rc; 
 50872  	SyBlobInit(&sWorker,&pOld->pHash->sAllocator);
 50873  	/* Perform the split */
 50874  	pCell = pOld->pList;
 50875  	for( ;; ){
 50876  		if( pCell == 0 ){
 50877  			/* No more cells */
 50878  			break;
 50879  		}
 50880  		/* Obtain the new logical bucket */
 50881  		iBucket = pCell->nHash & high_mask;
 50882  		pNext =  pCell->pNext;
 50883  		if( iBucket != split_bucket){
 50884  			rc = UNQLITE_OK;
 50885  			if( pCell->iOvfl ){
 50886  				/* Transfer the cell only */
 50887  				rc = lhTransferCell(pCell,pNew);
 50888  			}else{
 50889  				/* Transfer the cell and its payload */
 50890  				SyBlobReset(&sWorker);
 50891  				if( SyBlobLength(&pCell->sKey) < 1 ){
 50892  					/* Consume the key */
 50893  					rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,0);
 50894  					if( rc != UNQLITE_OK ){
 50895  						goto fail;
 50896  					}
 50897  				}
 50898  				/* Consume the data (Very small data < 65k) */
 50899  				rc = lhConsumeCellData(pCell,unqliteDataConsumer,&sWorker);
 50900  				if( rc != UNQLITE_OK ){
 50901  					goto fail;
 50902  				}
 50903  				/* Perform the transfer */
 50904  				rc = lhStoreCell(
 50905  					pNew,
 50906  					SyBlobData(&pCell->sKey),(int)SyBlobLength(&pCell->sKey),
 50907  					SyBlobData(&sWorker),SyBlobLength(&sWorker),
 50908  					pCell->nHash,
 50909  					1
 50910  					);
 50911  			}
 50912  			if( rc != UNQLITE_OK ){
 50913  				goto fail;
 50914  			}
 50915  			/* Discard the cell from the old page */
 50916  			lhUnlinkCell(pCell);
 50917  		}
 50918  		/* Point to the next cell */
 50919  		pCell = pNext;
 50920  	}
 50921  	/* All done */
 50922  	rc = UNQLITE_OK;
 50923  fail:
 50924  	SyBlobRelease(&sWorker);
 50925  	return rc;
 50926  }
 50927  /*
 50928   * Perform the infamous linear hash split operation.
 50929   */
 50930  static int lhSplit(lhpage *pTarget,int *pRetry)
 50931  {
 50932  	lhash_kv_engine *pEngine = pTarget->pHash;
 50933  	lhash_bmap_rec *pRec;
 50934  	lhpage *pOld,*pNew;
 50935  	unqlite_page *pRaw;
 50936  	int rc;
 50937  	/* Get the real page number of the bucket to split */
 50938  	pRec = lhMapFindBucket(pEngine,pEngine->split_bucket);
 50939  	if( pRec == 0 ){
 50940  		/* Can't happen */
 50941  		return UNQLITE_CORRUPT;
 50942  	}
 50943  	/* Load the page to be split */
 50944  	rc = lhLoadPage(pEngine,pRec->iReal,0,&pOld,0);
 50945  	if( rc != UNQLITE_OK ){
 50946  		return rc;
 50947  	}
 50948  	/* Request a new page */
 50949  	rc = lhAcquirePage(pEngine,&pRaw);
 50950  	if( rc != UNQLITE_OK ){
 50951  		return rc;
 50952  	}
 50953  	/* Initialize the page */
 50954  	pNew = lhNewPage(pEngine,pRaw,0);
 50955  	if( pNew == 0 ){
 50956  		return UNQLITE_NOMEM;
 50957  	}
 50958  	/* Mark as an empty page */
 50959  	rc = lhSetEmptyPage(pNew);
 50960  	if( rc != UNQLITE_OK ){
 50961  		goto fail;
 50962  	}
 50963  	/* Install and write the logical map record */
 50964  	rc = lhMapWriteRecord(pEngine,
 50965  		pEngine->split_bucket + pEngine->max_split_bucket,
 50966  		pRaw->pgno
 50967  		);
 50968  	if( rc != UNQLITE_OK ){
 50969  		goto fail;
 50970  	}
 50971  	if( pTarget->pRaw->pgno == pOld->pRaw->pgno ){
 50972  		*pRetry = 1;
 50973  	}
 50974  	/* Perform the split */
 50975  	rc = lhPageSplit(pOld,pNew,pEngine->split_bucket,pEngine->nmax_split_nucket - 1);
 50976  	if( rc != UNQLITE_OK ){
 50977  		goto fail;
 50978  	}
 50979  	/* Update the database header */
 50980  	pEngine->split_bucket++;
 50981  	/* Acquire a writer lock on the first page */
 50982  	rc = pEngine->pIo->xWrite(pEngine->pHeader);
 50983  	if( rc != UNQLITE_OK ){
 50984  		return rc;
 50985  	}
 50986  	if( pEngine->split_bucket >= pEngine->max_split_bucket ){
 50987  		/* Increment the generation number */
 50988  		pEngine->split_bucket = 0;
 50989  		pEngine->max_split_bucket = pEngine->nmax_split_nucket;
 50990  		pEngine->nmax_split_nucket <<= 1;
 50991  		if( !pEngine->nmax_split_nucket ){
 50992  			/* If this happen to your installation, please tell us <chm@symisc.net> */
 50993  			pEngine->pIo->xErr(pEngine->pIo->pHandle,"Database page (64-bit integer) limit reached");
 50994  			return UNQLITE_LIMIT;
 50995  		}
 50996  		/* Reflect in the page header */
 50997  		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
 50998  		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/+8/*Split bucket*/],pEngine->max_split_bucket);
 50999  	}else{
 51000  		/* Modify only the split bucket */
 51001  		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
 51002  	}
 51003  	/* All done */
 51004  	return UNQLITE_OK;
 51005  fail:
 51006  	pEngine->pIo->xPageUnref(pNew->pRaw);
 51007  	return rc;
 51008  }
 51009  /*
 51010   * Store a record in the target page.
 51011   */
 51012  static int lhRecordInstall(
 51013  	  lhpage *pPage, /* Target page */
 51014  	  sxu32 nHash,   /* Hash of the key */
 51015  	  const void *pKey,sxu32 nKeyLen,          /* Payload: Key */
 51016  	  const void *pData,unqlite_int64 nDataLen /* Payload: Data */
 51017  	  )
 51018  {
 51019  	int rc;
 51020  	rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,0);
 51021  	if( rc == UNQLITE_FULL ){
 51022  		int do_retry = 0;
 51023  		/* Split */
 51024  		rc = lhSplit(pPage,&do_retry);
 51025  		if( rc == UNQLITE_OK ){
 51026  			if( do_retry ){
 51027  				/* Re-calculate logical bucket number */
 51028  				return SXERR_RETRY;
 51029  			}
 51030  			/* Perform the store */
 51031  			rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
 51032  		}
 51033  	}
 51034  	return rc;
 51035  }
 51036  /*
 51037   * Insert a record (Either overwrite or append operation) in our database.
 51038   */
 51039  static int lh_record_insert(
 51040  	  unqlite_kv_engine *pKv,         /* KV store */
 51041  	  const void *pKey,sxu32 nKeyLen, /* Payload: Key */
 51042  	  const void *pData,unqlite_int64 nDataLen, /* Payload: data */
 51043  	  int is_append /* True for an append operation */
 51044  	  )
 51045  {
 51046  	lhash_kv_engine *pEngine = (lhash_kv_engine *)pKv;
 51047  	lhash_bmap_rec *pRec;
 51048  	unqlite_page *pRaw;
 51049  	lhpage *pPage;
 51050  	lhcell *pCell;
 51051  	pgno iBucket;
 51052  	sxu32 nHash;
 51053  	int iCnt;
 51054  	int rc;
 51055  
 51056  	/* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */
 51057  	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
 51058  	if( rc != UNQLITE_OK ){
 51059  		return rc;
 51060  	}
 51061  	iCnt = 0;
 51062  	/* Compute the hash of the key first */
 51063  	nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
 51064  retry:
 51065  	/* Extract the logical bucket number */
 51066  	iBucket = nHash & (pEngine->nmax_split_nucket - 1);
 51067  	if( iBucket >= pEngine->split_bucket + pEngine->max_split_bucket ){
 51068  		/* Low mask */
 51069  		iBucket = nHash & (pEngine->max_split_bucket - 1);
 51070  	}
 51071  	/* Map the logical bucket number to real page number */
 51072  	pRec = lhMapFindBucket(pEngine,iBucket);
 51073  	if( pRec == 0 ){
 51074  		/* Request a new page */
 51075  		rc = lhAcquirePage(pEngine,&pRaw);
 51076  		if( rc != UNQLITE_OK ){
 51077  			return rc;
 51078  		}
 51079  		/* Initialize the page */
 51080  		pPage = lhNewPage(pEngine,pRaw,0);
 51081  		if( pPage == 0 ){
 51082  			return UNQLITE_NOMEM;
 51083  		}
 51084  		/* Mark as an empty page */
 51085  		rc = lhSetEmptyPage(pPage);
 51086  		if( rc != UNQLITE_OK ){
 51087  			pEngine->pIo->xPageUnref(pRaw); /* pPage will be released during this call */
 51088  			return rc;
 51089  		}
 51090  		/* Store the cell */
 51091  		rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
 51092  		if( rc == UNQLITE_OK ){
 51093  			/* Install and write the logical map record */
 51094  			rc = lhMapWriteRecord(pEngine,iBucket,pRaw->pgno);
 51095  		}
 51096  		pEngine->pIo->xPageUnref(pRaw);
 51097  		return rc;
 51098  	}else{
 51099  		/* Load the page */
 51100  		rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
 51101  		if( rc != UNQLITE_OK ){
 51102  			/* IO error, unlikely scenario */
 51103  			return rc;
 51104  		}
 51105  		/* Do not add this page to the hot dirty list */
 51106  		pEngine->pIo->xDontMkHot(pPage->pRaw);
 51107  		/* Lookup for the cell */
 51108  		pCell = lhFindCell(pPage,pKey,(sxu32)nKeyLen,nHash);
 51109  		if( pCell == 0 ){
 51110  			/* Create the record */
 51111  			rc = lhRecordInstall(pPage,nHash,pKey,nKeyLen,pData,nDataLen);
 51112  			if( rc == SXERR_RETRY && iCnt++ < 2 ){
 51113  				rc = UNQLITE_OK;
 51114  				goto retry;
 51115  			}
 51116  		}else{
 51117  			if( is_append ){
 51118  				/* Append operation */
 51119  				rc = lhRecordAppend(pCell,pData,nDataLen);
 51120  			}else{
 51121  				/* Overwrite old value */
 51122  				rc = lhRecordOverwrite(pCell,pData,nDataLen);
 51123  			}
 51124  		}
 51125  		pEngine->pIo->xPageUnref(pPage->pRaw);
 51126  	}
 51127  	return rc;
 51128  }
 51129  /*
 51130   * Replace method.
 51131   */
 51132  static int lhash_kv_replace(
 51133  	  unqlite_kv_engine *pKv,
 51134  	  const void *pKey,int nKeyLen,
 51135  	  const void *pData,unqlite_int64 nDataLen
 51136  	  )
 51137  {
 51138  	int rc;
 51139  	rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,0);
 51140  	return rc;
 51141  }
 51142  /*
 51143   * Append method.
 51144   */
 51145  static int lhash_kv_append(
 51146  	  unqlite_kv_engine *pKv,
 51147  	  const void *pKey,int nKeyLen,
 51148  	  const void *pData,unqlite_int64 nDataLen
 51149  	  )
 51150  {
 51151  	int rc;
 51152  	rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,1);
 51153  	return rc;
 51154  }
 51155  /*
 51156   * Write the hash header (Page one).
 51157   */
 51158  static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
 51159  {
 51160  	unsigned char *zRaw = pHeader->zData;
 51161  	lhash_bmap_page *pMap;
 51162  
 51163  	pEngine->pHeader = pHeader;
 51164  	/* 4 byte magic number */
 51165  	SyBigEndianPack32(zRaw,pEngine->nMagic);
 51166  	zRaw += 4;
 51167  	/* 4 byte hash value to identify a valid hash function */
 51168  	SyBigEndianPack32(zRaw,pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1));
 51169  	zRaw += 4;
 51170  	/* List of free pages: Empty */
 51171  	SyBigEndianPack64(zRaw,0);
 51172  	zRaw += 8;
 51173  	/* Current split bucket */
 51174  	SyBigEndianPack64(zRaw,pEngine->split_bucket);
 51175  	zRaw += 8;
 51176  	/* Maximum split bucket */
 51177  	SyBigEndianPack64(zRaw,pEngine->max_split_bucket);
 51178  	zRaw += 8;
 51179  	/* Initialiaze the bucket map */
 51180  	pMap = &pEngine->sPageMap;
 51181  	/* Fill in the structure */
 51182  	pMap->iNum = pHeader->pgno;
 51183  	/* Next page in the bucket map */
 51184  	SyBigEndianPack64(zRaw,0);
 51185  	zRaw += 8;
 51186  	/* Total number of records in the bucket map */
 51187  	SyBigEndianPack32(zRaw,0);
 51188  	zRaw += 4;
 51189  	pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
 51190  	/* All done */
 51191  	return UNQLITE_OK;
 51192   }
 51193  /*
 51194   * Exported: xOpen() method.
 51195   */
 51196  static int lhash_kv_open(unqlite_kv_engine *pEngine,pgno dbSize)
 51197  {
 51198  	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
 51199  	unqlite_page *pHeader;
 51200  	int rc;
 51201  	if( dbSize < 1 ){
 51202  		/* A new database, create the header */
 51203  		rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pHeader);
 51204  		if( rc != UNQLITE_OK ){
 51205  			return rc;
 51206  		}
 51207  		/* Acquire a writer lock */
 51208  		rc = pEngine->pIo->xWrite(pHeader);
 51209  		if( rc != UNQLITE_OK ){
 51210  			return rc;
 51211  		}
 51212  		/* Write the hash header */
 51213  		rc = lhash_write_header(pHash,pHeader);
 51214  		if( rc != UNQLITE_OK ){
 51215  			return rc;
 51216  		}
 51217  	}else{
 51218  		/* Acquire the page one of the database */
 51219  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,&pHeader);
 51220  		if( rc != UNQLITE_OK ){
 51221  			return rc;
 51222  		}
 51223  		/* Read the database header */
 51224  		rc = lhash_read_header(pHash,pHeader);
 51225  		if( rc != UNQLITE_OK ){
 51226  			return rc;
 51227  		}
 51228  	}
 51229  	return UNQLITE_OK;
 51230  }
 51231  /*
 51232   * Release a master or slave page. (xUnpin callback).
 51233   */
 51234  static void lhash_page_release(void *pUserData)
 51235  {
 51236  	lhpage *pPage = (lhpage *)pUserData;
 51237  	lhash_kv_engine *pEngine = pPage->pHash;
 51238  	lhcell *pNext,*pCell = pPage->pList;
 51239  	unqlite_page *pRaw = pPage->pRaw;
 51240  	sxu32 n;
 51241  	/* Drop in-memory cells */
 51242  	for( n = 0 ; n < pPage->nCell ; ++n ){
 51243  		pNext = pCell->pNext;
 51244  		SyBlobRelease(&pCell->sKey);
 51245  		/* Release the cell instance */
 51246  		SyMemBackendPoolFree(&pEngine->sAllocator,(void *)pCell);
 51247  		/* Point to the next entry */
 51248  		pCell = pNext;
 51249  	}
 51250  	if( pPage->apCell ){
 51251  		/* Release the cell table */
 51252  		SyMemBackendFree(&pEngine->sAllocator,(void *)pPage->apCell);
 51253  	}
 51254  	/* Finally, release the whole page */
 51255  	SyMemBackendPoolFree(&pEngine->sAllocator,pPage);
 51256  	pRaw->pUserData = 0;
 51257  }
 51258  /*
 51259   * Default hash function (DJB).
 51260   */
 51261  static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen)
 51262  {
 51263  	register unsigned char *zIn = (unsigned char *)pSrc;
 51264  	unsigned char *zEnd;
 51265  	sxu32 nH = 5381;
 51266  	if( nLen > 2048 /* 2K */ ){
 51267  		nLen = 2048;
 51268  	}
 51269  	zEnd = &zIn[nLen];
 51270  	for(;;){
 51271  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 51272  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 51273  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 51274  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 51275  	}	
 51276  	return nH;
 51277  }
 51278  /*
 51279   * Exported: xInit() method.
 51280   * Initialize the Key value storage engine.
 51281   */
 51282  static int lhash_kv_init(unqlite_kv_engine *pEngine,int iPageSize)
 51283  {
 51284  	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
 51285  	int rc;
 51286  
 51287  	/* This structure is always zeroed, go to the initialization directly */
 51288  	SyMemBackendInitFromParent(&pHash->sAllocator,unqliteExportMemBackend());
 51289  #if defined(UNQLITE_ENABLE_THREADS)
 51290  	/* Already protected by the upper layers */
 51291  	SyMemBackendDisbaleMutexing(&pHash->sAllocator);
 51292  #endif
 51293  	pHash->iPageSize = iPageSize;
 51294  	/* Default hash function */
 51295  	pHash->xHash = lhash_bin_hash;
 51296  	/* Default comparison function */
 51297  	pHash->xCmp = SyMemcmp;
 51298  	/* Allocate a new record map */
 51299  	pHash->nBuckSize = 32;
 51300  	pHash->apMap = (lhash_bmap_rec **)SyMemBackendAlloc(&pHash->sAllocator,pHash->nBuckSize *sizeof(lhash_bmap_rec *));
 51301  	if( pHash->apMap == 0 ){
 51302  		rc = UNQLITE_NOMEM;
 51303  		goto err;
 51304  	}
 51305  	/* Zero the table */
 51306  	SyZero(pHash->apMap,pHash->nBuckSize * sizeof(lhash_bmap_rec *));
 51307  	/* Linear hashing components */
 51308  	pHash->split_bucket = 0; /* Logical not real bucket number */
 51309  	pHash->max_split_bucket = 1;
 51310  	pHash->nmax_split_nucket = 2;
 51311  	pHash->nMagic = L_HASH_MAGIC;
 51312  	/* Install the cache unpin and reload callbacks */
 51313  	pHash->pIo->xSetUnpin(pHash->pIo->pHandle,lhash_page_release);
 51314  	pHash->pIo->xSetReload(pHash->pIo->pHandle,lhash_page_release);
 51315  	return UNQLITE_OK;
 51316  err:
 51317  	SyMemBackendRelease(&pHash->sAllocator);
 51318  	return rc;
 51319  }
 51320  /*
 51321   * Exported: xRelease() method.
 51322   * Release the Key value storage engine.
 51323   */
 51324  static void lhash_kv_release(unqlite_kv_engine *pEngine)
 51325  {
 51326  	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
 51327  	/* Release the private memory backend */
 51328  	SyMemBackendRelease(&pHash->sAllocator);
 51329  }
 51330  /*
 51331   *  Exported: xConfig() method.
 51332   *  Configure the linear hash KV store.
 51333   */
 51334  static int lhash_kv_config(unqlite_kv_engine *pEngine,int op,va_list ap)
 51335  {
 51336  	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
 51337  	int rc = UNQLITE_OK;
 51338  	switch(op){
 51339  	case UNQLITE_KV_CONFIG_HASH_FUNC: {
 51340  		/* Default hash function */
 51341  		if( pHash->nBuckRec > 0 ){
 51342  			/* Locked operation */
 51343  			rc = UNQLITE_LOCKED;
 51344  		}else{
 51345  			ProcHash xHash = va_arg(ap,ProcHash);
 51346  			if( xHash ){
 51347  				pHash->xHash = xHash;
 51348  			}
 51349  		}
 51350  		break;
 51351  									  }
 51352  	case UNQLITE_KV_CONFIG_CMP_FUNC: {
 51353  		/* Default comparison function */
 51354  		ProcCmp xCmp = va_arg(ap,ProcCmp);
 51355  		if( xCmp ){
 51356  			pHash->xCmp  = xCmp;
 51357  		}
 51358  		break;
 51359  									 }
 51360  	default:
 51361  		/* Unknown OP */
 51362  		rc = UNQLITE_UNKNOWN;
 51363  		break;
 51364  	}
 51365  	return rc;
 51366  }
 51367  /*
 51368   * Each public cursor is identified by an instance of this structure.
 51369   */
 51370  typedef struct lhash_kv_cursor lhash_kv_cursor;
 51371  struct lhash_kv_cursor
 51372  {
 51373  	unqlite_kv_engine *pStore; /* Must be first */
 51374  	/* Private fields */
 51375  	int iState;           /* Current state of the cursor */
 51376  	int is_first;         /* True to read the database header */
 51377  	lhcell *pCell;        /* Current cell we are processing */
 51378  	unqlite_page *pRaw;   /* Raw disk page */
 51379  	lhash_bmap_rec *pRec; /* Logical to real bucket map */
 51380  };
 51381  /* 
 51382   * Possible state of the cursor
 51383   */
 51384  #define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */
 51385  #define L_HASH_CURSOR_STATE_CELL      2 /* Processing Cell */
 51386  #define L_HASH_CURSOR_STATE_DONE      3 /* Cursor does not point to anything */
 51387  /*
 51388   * Initialize the cursor.
 51389   */
 51390  static void lhInitCursor(unqlite_kv_cursor *pPtr)
 51391  {
 51392  	 lhash_kv_engine *pEngine = (lhash_kv_engine *)pPtr->pStore;
 51393  	 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
 51394  	 /* Init */
 51395  	 pCur->iState = L_HASH_CURSOR_STATE_NEXT_PAGE;
 51396  	 pCur->pCell = 0;
 51397  	 pCur->pRec = pEngine->pFirst;
 51398  	 pCur->pRaw = 0;
 51399  	 pCur->is_first = 1;
 51400  }
 51401  /*
 51402   * Point to the next page on the database.
 51403   */
 51404  static int lhCursorNextPage(lhash_kv_cursor *pPtr)
 51405  {
 51406  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
 51407  	lhash_bmap_rec *pRec;
 51408  	lhpage *pPage;
 51409  	int rc;
 51410  	for(;;){
 51411  		pRec = pCur->pRec;
 51412  		if( pRec == 0 ){
 51413  			pCur->iState = L_HASH_CURSOR_STATE_DONE;
 51414  			return UNQLITE_DONE;
 51415  		}
 51416  		if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
 51417  			/* Unref this page */
 51418  			pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
 51419  			pPtr->pRaw = 0;
 51420  		}
 51421  		/* Advance the map cursor */
 51422  		pCur->pRec = pRec->pPrev; /* Not a bug, reverse link */
 51423  		/* Load the next page on the list */
 51424  		rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
 51425  		if( rc != UNQLITE_OK ){
 51426  			return rc;
 51427  		}
 51428  		if( pPage->pList ){
 51429  			/* Reflect the change */
 51430  			pCur->pCell = pPage->pList;
 51431  			pCur->iState = L_HASH_CURSOR_STATE_CELL;
 51432  			pCur->pRaw = pPage->pRaw;
 51433  			break;
 51434  		}
 51435  		/* Empty page, discard this page and continue */
 51436  		pPage->pHash->pIo->xPageUnref(pPage->pRaw);
 51437  	}
 51438  	return UNQLITE_OK;
 51439  }
 51440  /*
 51441   * Point to the previous page on the database.
 51442   */
 51443  static int lhCursorPrevPage(lhash_kv_cursor *pPtr)
 51444  {
 51445  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
 51446  	lhash_bmap_rec *pRec;
 51447  	lhpage *pPage;
 51448  	int rc;
 51449  	for(;;){
 51450  		pRec = pCur->pRec;
 51451  		if( pRec == 0 ){
 51452  			pCur->iState = L_HASH_CURSOR_STATE_DONE;
 51453  			return UNQLITE_DONE;
 51454  		}
 51455  		if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
 51456  			/* Unref this page */
 51457  			pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
 51458  			pPtr->pRaw = 0;
 51459  		}
 51460  		/* Advance the map cursor */
 51461  		pCur->pRec = pRec->pNext; /* Not a bug, reverse link */
 51462  		/* Load the previous page on the list */
 51463  		rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
 51464  		if( rc != UNQLITE_OK ){
 51465  			return rc;
 51466  		}
 51467  		if( pPage->pFirst ){
 51468  			/* Reflect the change */
 51469  			pCur->pCell = pPage->pFirst;
 51470  			pCur->iState = L_HASH_CURSOR_STATE_CELL;
 51471  			pCur->pRaw = pPage->pRaw;
 51472  			break;
 51473  		}
 51474  		/* Discard this page and continue */
 51475  		pPage->pHash->pIo->xPageUnref(pPage->pRaw);
 51476  	}
 51477  	return UNQLITE_OK;
 51478  }
 51479  /*
 51480   * Is a valid cursor.
 51481   */
 51482  static int lhCursorValid(unqlite_kv_cursor *pPtr)
 51483  {
 51484  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
 51485  	return (pCur->iState == L_HASH_CURSOR_STATE_CELL) && pCur->pCell;
 51486  }
 51487  /*
 51488   * Point to the first record.
 51489   */
 51490  static int lhCursorFirst(unqlite_kv_cursor *pCursor)
 51491  {
 51492  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51493  	lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
 51494  	int rc;
 51495  	if( pCur->is_first ){
 51496  		/* Read the database header first */
 51497  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
 51498  		if( rc != UNQLITE_OK ){
 51499  			return rc;
 51500  		}
 51501  		pCur->is_first = 0;
 51502  	}
 51503  	/* Point to the first map record */
 51504  	pCur->pRec = pEngine->pFirst;
 51505  	/* Load the cells */
 51506  	rc = lhCursorNextPage(pCur);
 51507  	return rc;
 51508  }
 51509  /*
 51510   * Point to the last record.
 51511   */
 51512  static int lhCursorLast(unqlite_kv_cursor *pCursor)
 51513  {
 51514  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51515  	lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
 51516  	int rc;
 51517  	if( pCur->is_first ){
 51518  		/* Read the database header first */
 51519  		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
 51520  		if( rc != UNQLITE_OK ){
 51521  			return rc;
 51522  		}
 51523  		pCur->is_first = 0;
 51524  	}
 51525  	/* Point to the last map record */
 51526  	pCur->pRec = pEngine->pList;
 51527  	/* Load the cells */
 51528  	rc = lhCursorPrevPage(pCur);
 51529  	return rc;
 51530  }
 51531  /*
 51532   * Reset the cursor.
 51533   */
 51534  static void lhCursorReset(unqlite_kv_cursor *pCursor)
 51535  {
 51536  	lhCursorFirst(pCursor);
 51537  }
 51538  /*
 51539   * Point to the next record.
 51540   */
 51541  static int lhCursorNext(unqlite_kv_cursor *pCursor)
 51542  {
 51543  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51544  	lhcell *pCell;
 51545  	int rc;
 51546  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51547  		/* Load the cells of the next page  */
 51548  		rc = lhCursorNextPage(pCur);
 51549  		return rc;
 51550  	}
 51551  	pCell = pCur->pCell;
 51552  	pCur->pCell = pCell->pNext;
 51553  	if( pCur->pCell == 0 ){
 51554  		/* Load the cells of the next page  */
 51555  		rc = lhCursorNextPage(pCur);
 51556  		return rc;
 51557  	}
 51558  	return UNQLITE_OK;
 51559  }
 51560  /*
 51561   * Point to the previous record.
 51562   */
 51563  static int lhCursorPrev(unqlite_kv_cursor *pCursor)
 51564  {
 51565  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51566  	lhcell *pCell;
 51567  	int rc;
 51568  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51569  		/* Load the cells of the previous page  */
 51570  		rc = lhCursorPrevPage(pCur);
 51571  		return rc;
 51572  	}
 51573  	pCell = pCur->pCell;
 51574  	pCur->pCell = pCell->pPrev;
 51575  	if( pCur->pCell == 0 ){
 51576  		/* Load the cells of the previous page  */
 51577  		rc = lhCursorPrevPage(pCur);
 51578  		return rc;
 51579  	}
 51580  	return UNQLITE_OK;
 51581  }
 51582  /*
 51583   * Return key length.
 51584   */
 51585  static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
 51586  {
 51587  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51588  	lhcell *pCell;
 51589  	
 51590  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51591  		/* Invalid state */
 51592  		return UNQLITE_INVALID;
 51593  	}
 51594  	/* Point to the target cell */
 51595  	pCell = pCur->pCell;
 51596  	/* Return key length */
 51597  	*pLen = (int)pCell->nKey;
 51598  	return UNQLITE_OK;
 51599  }
 51600  /*
 51601   * Return data length.
 51602   */
 51603  static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
 51604  {
 51605  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51606  	lhcell *pCell;
 51607  	
 51608  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51609  		/* Invalid state */
 51610  		return UNQLITE_INVALID;
 51611  	}
 51612  	/* Point to the target cell */
 51613  	pCell = pCur->pCell;
 51614  	/* Return data length */
 51615  	*pLen = (unqlite_int64)pCell->nData;
 51616  	return UNQLITE_OK;
 51617  }
 51618  /*
 51619   * Consume the key.
 51620   */
 51621  static int lhCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
 51622  {
 51623  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51624  	lhcell *pCell;
 51625  	int rc;
 51626  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51627  		/* Invalid state */
 51628  		return UNQLITE_INVALID;
 51629  	}
 51630  	/* Point to the target cell */
 51631  	pCell = pCur->pCell;
 51632  	if( SyBlobLength(&pCell->sKey) > 0 ){
 51633  		/* Consume the key directly */
 51634  		rc = xConsumer(SyBlobData(&pCell->sKey),SyBlobLength(&pCell->sKey),pUserData);
 51635  	}else{
 51636  		/* Very large key */
 51637  		rc = lhConsumeCellkey(pCell,xConsumer,pUserData,0);
 51638  	}
 51639  	return rc;
 51640  }
 51641  /*
 51642   * Consume the data.
 51643   */
 51644  static int lhCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
 51645  {
 51646  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51647  	lhcell *pCell;
 51648  	int rc;
 51649  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51650  		/* Invalid state */
 51651  		return UNQLITE_INVALID;
 51652  	}
 51653  	/* Point to the target cell */
 51654  	pCell = pCur->pCell;
 51655  	/* Consume the data */
 51656  	rc = lhConsumeCellData(pCell,xConsumer,pUserData);
 51657  	return rc;
 51658  }
 51659  /*
 51660   * Find a partiuclar record.
 51661   */
 51662  static int lhCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
 51663  {
 51664  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51665  	int rc;
 51666  	/* Perform a lookup */
 51667  	rc = lhRecordLookup((lhash_kv_engine *)pCur->pStore,pKey,nByte,&pCur->pCell);
 51668  	if( rc != UNQLITE_OK ){
 51669  		SXUNUSED(iPos);
 51670  		pCur->pCell = 0;
 51671  		pCur->iState = L_HASH_CURSOR_STATE_DONE;
 51672  		return rc;
 51673  	}
 51674  	pCur->iState = L_HASH_CURSOR_STATE_CELL;
 51675  	return UNQLITE_OK;
 51676  }
 51677  /*
 51678   * Remove a particular record.
 51679   */
 51680  static int lhCursorDelete(unqlite_kv_cursor *pCursor)
 51681  {
 51682  	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
 51683  	lhcell *pCell;
 51684  	int rc;
 51685  	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
 51686  		/* Invalid state */
 51687  		return UNQLITE_INVALID;
 51688  	}
 51689  	/* Point to the target cell  */
 51690  	pCell = pCur->pCell;
 51691  	/* Point to the next entry */
 51692  	pCur->pCell = pCell->pNext;
 51693  	/* Perform the deletion */
 51694  	rc = lhRecordRemove(pCell);
 51695  	return rc;
 51696  }
 51697  /*
 51698   * Export the linear-hash storage engine.
 51699   */
 51700  UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void)
 51701  {
 51702  	static const unqlite_kv_methods sDiskStore = {
 51703  		"hash",                     /* zName */
 51704  		sizeof(lhash_kv_engine),    /* szKv */
 51705  		sizeof(lhash_kv_cursor),    /* szCursor */
 51706  		1,                          /* iVersion */
 51707  		lhash_kv_init,              /* xInit */
 51708  		lhash_kv_release,           /* xRelease */
 51709  		lhash_kv_config,            /* xConfig */
 51710  		lhash_kv_open,              /* xOpen */
 51711  		lhash_kv_replace,           /* xReplace */
 51712  		lhash_kv_append,            /* xAppend */
 51713  		lhInitCursor,               /* xCursorInit */
 51714  		lhCursorSeek,               /* xSeek */
 51715  		lhCursorFirst,              /* xFirst */
 51716  		lhCursorLast,               /* xLast */
 51717  		lhCursorValid,              /* xValid */
 51718  		lhCursorNext,               /* xNext */
 51719  		lhCursorPrev,               /* xPrev */
 51720  		lhCursorDelete,             /* xDelete */
 51721  		lhCursorKeyLength,          /* xKeyLength */
 51722  		lhCursorKey,                /* xKey */
 51723  		lhCursorDataLength,         /* xDataLength */
 51724  		lhCursorData,               /* xData */
 51725  		lhCursorReset,              /* xReset */
 51726  		0                           /* xRelease */                        
 51727  	};
 51728  	return &sDiskStore;
 51729  }
 51730  /*
 51731   * ----------------------------------------------------------
 51732   * File: mem_kv.c
 51733   * MD5: 32e2610c95f53038114d9566f0d0489e
 51734   * ----------------------------------------------------------
 51735   */
 51736  /*
 51737   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 51738   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 51739   * Version 1.1.6
 51740   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 51741   * please contact Symisc Systems via:
 51742   *       legal@symisc.net
 51743   *       licensing@symisc.net
 51744   *       contact@symisc.net
 51745   * or visit:
 51746   *      http://unqlite.org/licensing.html
 51747   */
 51748   /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable <chm@symisc.net> $ */
 51749  #ifndef UNQLITE_AMALGAMATION
 51750  #include "unqliteInt.h"
 51751  #endif
 51752  /* 
 51753   * This file implements an in-memory key value storage engine for unQLite.
 51754   * Note that this storage engine does not support transactions.
 51755   *
 51756   * Normaly, I (chm@symisc.net) planned to implement a red-black tree
 51757   * which is suitable for this kind of operation, but due to the lack
 51758   * of time, I decided to implement a tunned hashtable which everybody
 51759   * know works very well for this kind of operation.
 51760   * Again, I insist on a red-black tree implementation for future version
 51761   * of Unqlite.
 51762   */
 51763  /* Forward declaration */
 51764  typedef struct mem_hash_kv_engine mem_hash_kv_engine;
 51765  /*
 51766   * Each record is storead in an instance of the following structure.
 51767   */
 51768  typedef struct mem_hash_record mem_hash_record;
 51769  struct mem_hash_record
 51770  {
 51771  	mem_hash_kv_engine *pEngine;    /* Storage engine */
 51772  	sxu32 nHash;                    /* Hash of the key */
 51773  	const void *pKey;               /* Key */
 51774  	sxu32 nKeyLen;                  /* Key size (Max 1GB) */
 51775  	const void *pData;              /* Data */
 51776  	sxu32 nDataLen;                 /* Data length (Max 4GB) */
 51777  	mem_hash_record *pNext,*pPrev;  /* Link to other records */
 51778  	mem_hash_record *pNextHash,*pPrevHash; /* Collision link */
 51779  };
 51780  /*
 51781   * Each in-memory KV engine is represented by an instance
 51782   * of the following structure.
 51783   */
 51784  struct mem_hash_kv_engine
 51785  {
 51786  	const unqlite_kv_io *pIo; /* IO methods: MUST be first */
 51787  	/* Private data */
 51788  	SyMemBackend sAlloc;        /* Private memory allocator */
 51789  	ProcHash    xHash;          /* Default hash function */
 51790  	ProcCmp     xCmp;           /* Default comparison function */
 51791  	sxu32 nRecord;              /* Total number of records  */
 51792  	sxu32 nBucket;              /* Bucket size: Must be a power of two */
 51793  	mem_hash_record **apBucket; /* Hash bucket */
 51794  	mem_hash_record *pFirst;    /* First inserted entry */
 51795  	mem_hash_record *pLast;     /* Last inserted entry */
 51796  };
 51797  /*
 51798   * Allocate a new hash record.
 51799   */
 51800  static mem_hash_record * MemHashNewRecord(
 51801  	mem_hash_kv_engine *pEngine,
 51802  	const void *pKey,int nKey,
 51803  	const void *pData,unqlite_int64 nData,
 51804  	sxu32 nHash
 51805  	)
 51806  {
 51807  	SyMemBackend *pAlloc = &pEngine->sAlloc;
 51808  	mem_hash_record *pRecord;
 51809  	void *pDupData;
 51810  	sxu32 nByte;
 51811  	char *zPtr;
 51812  	
 51813  	/* Total number of bytes to alloc */
 51814  	nByte = sizeof(mem_hash_record) + nKey;
 51815  	/* Allocate a new instance */
 51816  	pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte);
 51817  	if( pRecord == 0 ){
 51818  		return 0;
 51819  	}
 51820  	pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData);
 51821  	if( pDupData == 0 ){
 51822  		SyMemBackendFree(pAlloc,pRecord);
 51823  		return 0;
 51824  	}
 51825  	zPtr = (char *)pRecord;
 51826  	zPtr += sizeof(mem_hash_record);
 51827  	/* Zero the structure */
 51828  	SyZero(pRecord,sizeof(mem_hash_record));
 51829  	/* Fill in the structure */
 51830  	pRecord->pEngine = pEngine;
 51831  	pRecord->nDataLen = (sxu32)nData;
 51832  	pRecord->nKeyLen = (sxu32)nKey;
 51833  	pRecord->nHash = nHash;
 51834  	SyMemcpy(pKey,zPtr,pRecord->nKeyLen);
 51835  	pRecord->pKey = (const void *)zPtr;
 51836  	SyMemcpy(pData,pDupData,pRecord->nDataLen);
 51837  	pRecord->pData = pDupData;
 51838  	/* All done */
 51839  	return pRecord;
 51840  }
 51841  /*
 51842   * Install a given record in the hashtable.
 51843   */
 51844  static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord)
 51845  {
 51846  	sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1);
 51847  	pRecord->pNextHash = pEngine->apBucket[nBucket];
 51848  	if( pEngine->apBucket[nBucket] ){
 51849  		pEngine->apBucket[nBucket]->pPrevHash = pRecord;
 51850  	}
 51851  	pEngine->apBucket[nBucket] = pRecord;
 51852  	if( pEngine->pFirst == 0 ){
 51853  		pEngine->pFirst = pEngine->pLast = pRecord;
 51854  	}else{
 51855  		MACRO_LD_PUSH(pEngine->pLast,pRecord);
 51856  	}
 51857  	pEngine->nRecord++;
 51858  }
 51859  /*
 51860   * Unlink a given record from the hashtable.
 51861   */
 51862  static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry)
 51863  {
 51864  	sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1);
 51865  	SyMemBackend *pAlloc = &pEngine->sAlloc;
 51866  	if( pEntry->pPrevHash == 0 ){
 51867  		pEngine->apBucket[nBucket] = pEntry->pNextHash;
 51868  	}else{
 51869  		pEntry->pPrevHash->pNextHash = pEntry->pNextHash;
 51870  	}
 51871  	if( pEntry->pNextHash ){
 51872  		pEntry->pNextHash->pPrevHash = pEntry->pPrevHash;
 51873  	}
 51874  	MACRO_LD_REMOVE(pEngine->pLast,pEntry);
 51875  	if( pEntry == pEngine->pFirst ){
 51876  		pEngine->pFirst = pEntry->pPrev;
 51877  	}
 51878  	pEngine->nRecord--;
 51879  	/* Release the entry */
 51880  	SyMemBackendFree(pAlloc,(void *)pEntry->pData);
 51881  	SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */
 51882  }
 51883  /*
 51884   * Perform a lookup for a given entry.
 51885   */
 51886  static mem_hash_record * MemHashGetEntry(
 51887  	mem_hash_kv_engine *pEngine,
 51888  	const void *pKey,int nKeyLen
 51889  	)
 51890  {
 51891  	mem_hash_record *pEntry;
 51892  	sxu32 nHash,nBucket;
 51893  	/* Hash the entry */
 51894  	nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
 51895  	nBucket = nHash & (pEngine->nBucket - 1);
 51896  	pEntry = pEngine->apBucket[nBucket];
 51897  	for(;;){
 51898  		if( pEntry == 0 ){
 51899  			break;
 51900  		}
 51901  		if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen && 
 51902  			pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){
 51903  				return pEntry;
 51904  		}
 51905  		pEntry = pEntry->pNextHash;
 51906  	}
 51907  	/* No such entry */
 51908  	return 0;
 51909  }
 51910  /*
 51911   * Rehash all the entries in the given table.
 51912   */
 51913  static int MemHashGrowTable(mem_hash_kv_engine *pEngine)
 51914  {
 51915  	sxu32 nNewSize = pEngine->nBucket << 1;
 51916  	mem_hash_record *pEntry;
 51917  	mem_hash_record **apNew;
 51918  	sxu32 n,iBucket;
 51919  	/* Allocate a new larger table */
 51920  	apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *));
 51921  	if( apNew == 0 ){
 51922  		/* Not so fatal, simply a performance hit */
 51923  		return UNQLITE_OK;
 51924  	}
 51925  	/* Zero the new table */
 51926  	SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *));
 51927  	/* Rehash all entries */
 51928  	n = 0;
 51929  	pEntry = pEngine->pLast;
 51930  	for(;;){
 51931  		
 51932  		/* Loop one */
 51933  		if( n >= pEngine->nRecord ){
 51934  			break;
 51935  		}
 51936  		pEntry->pNextHash = pEntry->pPrevHash = 0;
 51937  		/* Install in the new bucket */
 51938  		iBucket = pEntry->nHash & (nNewSize - 1);
 51939  		pEntry->pNextHash = apNew[iBucket];
 51940  		if( apNew[iBucket] ){
 51941  			apNew[iBucket]->pPrevHash = pEntry;
 51942  		}
 51943  		apNew[iBucket] = pEntry;
 51944  		/* Point to the next entry */
 51945  		pEntry = pEntry->pNext;
 51946  		n++;
 51947  
 51948  		/* Loop two */
 51949  		if( n >= pEngine->nRecord ){
 51950  			break;
 51951  		}
 51952  		pEntry->pNextHash = pEntry->pPrevHash = 0;
 51953  		/* Install in the new bucket */
 51954  		iBucket = pEntry->nHash & (nNewSize - 1);
 51955  		pEntry->pNextHash = apNew[iBucket];
 51956  		if( apNew[iBucket] ){
 51957  			apNew[iBucket]->pPrevHash = pEntry;
 51958  		}
 51959  		apNew[iBucket] = pEntry;
 51960  		/* Point to the next entry */
 51961  		pEntry = pEntry->pNext;
 51962  		n++;
 51963  
 51964  		/* Loop three */
 51965  		if( n >= pEngine->nRecord ){
 51966  			break;
 51967  		}
 51968  		pEntry->pNextHash = pEntry->pPrevHash = 0;
 51969  		/* Install in the new bucket */
 51970  		iBucket = pEntry->nHash & (nNewSize - 1);
 51971  		pEntry->pNextHash = apNew[iBucket];
 51972  		if( apNew[iBucket] ){
 51973  			apNew[iBucket]->pPrevHash = pEntry;
 51974  		}
 51975  		apNew[iBucket] = pEntry;
 51976  		/* Point to the next entry */
 51977  		pEntry = pEntry->pNext;
 51978  		n++;
 51979  
 51980  		/* Loop four */
 51981  		if( n >= pEngine->nRecord ){
 51982  			break;
 51983  		}
 51984  		pEntry->pNextHash = pEntry->pPrevHash = 0;
 51985  		/* Install in the new bucket */
 51986  		iBucket = pEntry->nHash & (nNewSize - 1);
 51987  		pEntry->pNextHash = apNew[iBucket];
 51988  		if( apNew[iBucket] ){
 51989  			apNew[iBucket]->pPrevHash = pEntry;
 51990  		}
 51991  		apNew[iBucket] = pEntry;
 51992  		/* Point to the next entry */
 51993  		pEntry = pEntry->pNext;
 51994  		n++;
 51995  	}
 51996  	/* Release the old table and reflect the change */
 51997  	SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket);
 51998  	pEngine->apBucket = apNew;
 51999  	pEngine->nBucket  = nNewSize;
 52000  	return UNQLITE_OK;
 52001  }
 52002  /*
 52003   * Exported Interfaces.
 52004   */
 52005  /*
 52006   * Each public cursor is identified by an instance of this structure.
 52007   */
 52008  typedef struct mem_hash_cursor mem_hash_cursor;
 52009  struct mem_hash_cursor
 52010  {
 52011  	unqlite_kv_engine *pStore; /* Must be first */
 52012  	/* Private fields */
 52013  	mem_hash_record *pCur;     /* Current hash record */
 52014  };
 52015  /*
 52016   * Initialize the cursor.
 52017   */
 52018  static void MemHashInitCursor(unqlite_kv_cursor *pCursor)
 52019  {
 52020  	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
 52021  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52022  	 /* Point to the first inserted entry */
 52023  	 pMem->pCur = pEngine->pFirst;
 52024  }
 52025  /*
 52026   * Point to the first entry.
 52027   */
 52028  static int MemHashCursorFirst(unqlite_kv_cursor *pCursor)
 52029  {
 52030  	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
 52031  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52032  	 pMem->pCur = pEngine->pFirst;
 52033  	 return UNQLITE_OK;
 52034  }
 52035  /*
 52036   * Point to the last entry.
 52037   */
 52038  static int MemHashCursorLast(unqlite_kv_cursor *pCursor)
 52039  {
 52040  	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
 52041  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52042  	 pMem->pCur = pEngine->pLast;
 52043  	 return UNQLITE_OK;
 52044  }
 52045  /*
 52046   * is a Valid Cursor.
 52047   */
 52048  static int MemHashCursorValid(unqlite_kv_cursor *pCursor)
 52049  {
 52050  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52051  	 return pMem->pCur != 0 ? 1 : 0;
 52052  }
 52053  /*
 52054   * Point to the next entry.
 52055   */
 52056  static int MemHashCursorNext(unqlite_kv_cursor *pCursor)
 52057  {
 52058  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52059  	 if( pMem->pCur == 0){
 52060  		 return UNQLITE_EOF;
 52061  	 }
 52062  	 pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */
 52063  	 return UNQLITE_OK;
 52064  }
 52065  /*
 52066   * Point to the previous entry.
 52067   */
 52068  static int MemHashCursorPrev(unqlite_kv_cursor *pCursor)
 52069  {
 52070  	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52071  	 if( pMem->pCur == 0){
 52072  		 return UNQLITE_EOF;
 52073  	 }
 52074  	 pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */
 52075  	 return UNQLITE_OK;
 52076  }
 52077  /*
 52078   * Return key length.
 52079   */
 52080  static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
 52081  {
 52082  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52083  	if( pMem->pCur == 0){
 52084  		 return UNQLITE_EOF;
 52085  	}
 52086  	*pLen = (int)pMem->pCur->nKeyLen;
 52087  	return UNQLITE_OK;
 52088  }
 52089  /*
 52090   * Return data length.
 52091   */
 52092  static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
 52093  {
 52094  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52095  	if( pMem->pCur == 0 ){
 52096  		 return UNQLITE_EOF;
 52097  	}
 52098  	*pLen = pMem->pCur->nDataLen;
 52099  	return UNQLITE_OK;
 52100  }
 52101  /*
 52102   * Consume the key.
 52103   */
 52104  static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
 52105  {
 52106  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52107  	int rc;
 52108  	if( pMem->pCur == 0){
 52109  		 return UNQLITE_EOF;
 52110  	}
 52111  	/* Invoke the callback */
 52112  	rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData);
 52113  	/* Callback result */
 52114  	return rc;
 52115  }
 52116  /*
 52117   * Consume the data.
 52118   */
 52119  static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
 52120  {
 52121  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52122  	int rc;
 52123  	if( pMem->pCur == 0){
 52124  		 return UNQLITE_EOF;
 52125  	}
 52126  	/* Invoke the callback */
 52127  	rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData);
 52128  	/* Callback result */
 52129  	return rc;
 52130  }
 52131  /*
 52132   * Reset the cursor.
 52133   */
 52134  static void MemHashCursorReset(unqlite_kv_cursor *pCursor)
 52135  {
 52136  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52137  	pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst;
 52138  }
 52139  /*
 52140   * Remove a particular record.
 52141   */
 52142  static int MemHashCursorDelete(unqlite_kv_cursor *pCursor)
 52143  {
 52144  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52145  	mem_hash_record *pNext;
 52146  	if( pMem->pCur == 0 ){
 52147  		/* Cursor does not point to anything */
 52148  		return UNQLITE_NOTFOUND;
 52149  	}
 52150  	pNext = pMem->pCur->pPrev;
 52151  	/* Perform the deletion */
 52152  	MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur);
 52153  	/* Point to the next entry */
 52154  	pMem->pCur = pNext;
 52155  	return UNQLITE_OK;
 52156  }
 52157  /*
 52158   * Find a particular record.
 52159   */
 52160  static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
 52161  {
 52162  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
 52163  	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
 52164  	/* Perform the lookup */
 52165  	pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte);
 52166  	if( pMem->pCur == 0 ){
 52167  		if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){
 52168  			/* noop; */
 52169  		}
 52170  		/* No such record */
 52171  		return UNQLITE_NOTFOUND;
 52172  	}
 52173  	return UNQLITE_OK;
 52174  }
 52175  /*
 52176   * Builtin hash function.
 52177   */
 52178  static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen)
 52179  {
 52180  	register unsigned char *zIn = (unsigned char *)pSrc;
 52181  	unsigned char *zEnd;
 52182  	sxu32 nH = 5381;
 52183  	zEnd = &zIn[nLen];
 52184  	for(;;){
 52185  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 52186  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 52187  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 52188  		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
 52189  	}	
 52190  	return nH;
 52191  }
 52192  /* Default bucket size */
 52193  #define MEM_HASH_BUCKET_SIZE 64
 52194  /* Default fill factor */
 52195  #define MEM_HASH_FILL_FACTOR 4 /* or 3 */
 52196  /*
 52197   * Initialize the in-memory storage engine.
 52198   */
 52199  static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize)
 52200  {
 52201  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
 52202  	/* Note that this instance is already zeroed */	
 52203  	/* Memory backend */
 52204  	SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend());
 52205  #if defined(UNQLITE_ENABLE_THREADS)
 52206  	/* Already protected by the upper layers */
 52207  	SyMemBackendDisbaleMutexing(&pEngine->sAlloc);
 52208  #endif
 52209  	/* Default hash & comparison function */
 52210  	pEngine->xHash = MemHashFunc;
 52211  	pEngine->xCmp = SyMemcmp;
 52212  	/* Allocate a new bucket */
 52213  	pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
 52214  	if( pEngine->apBucket == 0 ){
 52215  		SXUNUSED(iPageSize); /* cc warning */
 52216  		return UNQLITE_NOMEM;
 52217  	}
 52218  	/* Zero the bucket */
 52219  	SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
 52220  	pEngine->nRecord = 0;
 52221  	pEngine->nBucket = MEM_HASH_BUCKET_SIZE;
 52222  	return UNQLITE_OK;
 52223  }
 52224  /*
 52225   * Release the in-memory storage engine.
 52226   */
 52227  static void MemHashRelease(unqlite_kv_engine *pKvEngine)
 52228  {
 52229  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
 52230  	/* Release the private memory backend */
 52231  	SyMemBackendRelease(&pEngine->sAlloc);
 52232  }
 52233  /*
 52234   * Configure the in-memory storage engine.
 52235   */
 52236  static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap)
 52237  {
 52238  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
 52239  	int rc = UNQLITE_OK;
 52240  	switch(iOp){
 52241  	case UNQLITE_KV_CONFIG_HASH_FUNC:{
 52242  		/* Use a default hash function */
 52243  		if( pEngine->nRecord > 0 ){
 52244  			rc = UNQLITE_LOCKED;
 52245  		}else{
 52246  			ProcHash xHash = va_arg(ap,ProcHash);
 52247  			if( xHash ){
 52248  				pEngine->xHash = xHash;
 52249  			}
 52250  		}
 52251  		break;
 52252  									 }
 52253  	case UNQLITE_KV_CONFIG_CMP_FUNC: {
 52254  		/* Default comparison function */
 52255  		ProcCmp xCmp = va_arg(ap,ProcCmp);
 52256  		if( xCmp ){
 52257  			pEngine->xCmp = xCmp;
 52258  		}
 52259  		break;
 52260  									 }
 52261  	default:
 52262  		/* Unknown configuration option */
 52263  		rc = UNQLITE_UNKNOWN;
 52264  	}
 52265  	return rc;
 52266  }
 52267  /*
 52268   * Replace method.
 52269   */
 52270  static int MemHashReplace(
 52271  	  unqlite_kv_engine *pKv,
 52272  	  const void *pKey,int nKeyLen,
 52273  	  const void *pData,unqlite_int64 nDataLen
 52274  	  )
 52275  {
 52276  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
 52277  	mem_hash_record *pRecord;
 52278  	if( nDataLen > SXU32_HIGH ){
 52279  		/* Database limit */
 52280  		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
 52281  		return UNQLITE_LIMIT;
 52282  	}
 52283  	/* Fetch the record first */
 52284  	pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
 52285  	if( pRecord == 0 ){
 52286  		/* Allocate a new record */
 52287  		pRecord = MemHashNewRecord(pEngine,
 52288  			pKey,nKeyLen,
 52289  			pData,nDataLen,
 52290  			pEngine->xHash(pKey,nKeyLen)
 52291  			);
 52292  		if( pRecord == 0 ){
 52293  			return UNQLITE_NOMEM;
 52294  		}
 52295  		/* Link the entry */
 52296  		MemHashLinkRecord(pEngine,pRecord);
 52297  		if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){
 52298  			/* Rehash the table */
 52299  			MemHashGrowTable(pEngine);
 52300  		}
 52301  	}else{
 52302  		sxu32 nData = (sxu32)nDataLen;
 52303  		void *pNew;
 52304  		/* Replace an existing record */
 52305  		if( nData == pRecord->nDataLen ){
 52306  			/* No need to free the old chunk */
 52307  			pNew = (void *)pRecord->pData;
 52308  		}else{
 52309  			pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData);
 52310  			if( pNew == 0 ){
 52311  				return UNQLITE_NOMEM;
 52312  			}
 52313  			/* Release the old data */
 52314  			SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData);
 52315  		}
 52316  		/* Reflect the change */
 52317  		pRecord->nDataLen = nData;
 52318  		SyMemcpy(pData,pNew,nData);
 52319  		pRecord->pData = pNew;
 52320  	}
 52321  	return UNQLITE_OK;
 52322  }
 52323  /*
 52324   * Append method.
 52325   */
 52326  static int MemHashAppend(
 52327  	  unqlite_kv_engine *pKv,
 52328  	  const void *pKey,int nKeyLen,
 52329  	  const void *pData,unqlite_int64 nDataLen
 52330  	  )
 52331  {
 52332  	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
 52333  	mem_hash_record *pRecord;
 52334  	if( nDataLen > SXU32_HIGH ){
 52335  		/* Database limit */
 52336  		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
 52337  		return UNQLITE_LIMIT;
 52338  	}
 52339  	/* Fetch the record first */
 52340  	pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
 52341  	if( pRecord == 0 ){
 52342  		/* Allocate a new record */
 52343  		pRecord = MemHashNewRecord(pEngine,
 52344  			pKey,nKeyLen,
 52345  			pData,nDataLen,
 52346  			pEngine->xHash(pKey,nKeyLen)
 52347  			);
 52348  		if( pRecord == 0 ){
 52349  			return UNQLITE_NOMEM;
 52350  		}
 52351  		/* Link the entry */
 52352  		MemHashLinkRecord(pEngine,pRecord);
 52353  		if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){
 52354  			/* Rehash the table */
 52355  			MemHashGrowTable(pEngine);
 52356  		}
 52357  	}else{
 52358  		unqlite_int64 nNew = pRecord->nDataLen + nDataLen;
 52359  		void *pOld = (void *)pRecord->pData;
 52360  		sxu32 nData;
 52361  		char *zNew;
 52362  		/* Append data to the existing record */
 52363  		if( nNew > SXU32_HIGH ){
 52364  			/* Overflow */
 52365  			pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");	
 52366  			return UNQLITE_LIMIT;
 52367  		}
 52368  		nData = (sxu32)nNew;
 52369  		/* Allocate bigger chunk */
 52370  		zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData);
 52371  		if( zNew == 0 ){
 52372  			return UNQLITE_NOMEM;
 52373  		}
 52374  		/* Reflect the change */
 52375  		SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen);
 52376  		pRecord->pData = (const void *)zNew;
 52377  		pRecord->nDataLen = nData;
 52378  	}
 52379  	return UNQLITE_OK;
 52380  }
 52381  /*
 52382   * Export the in-memory storage engine.
 52383   */
 52384  UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void)
 52385  {
 52386  	static const unqlite_kv_methods sMemStore = {
 52387  		"mem",                      /* zName */
 52388  		sizeof(mem_hash_kv_engine), /* szKv */
 52389  		sizeof(mem_hash_cursor),    /* szCursor */
 52390  		1,                          /* iVersion */
 52391  		MemHashInit,                /* xInit */
 52392  		MemHashRelease,             /* xRelease */
 52393  		MemHashConfigure,           /* xConfig */
 52394  		0,                          /* xOpen */
 52395  		MemHashReplace,             /* xReplace */
 52396  		MemHashAppend,              /* xAppend */
 52397  		MemHashInitCursor,          /* xCursorInit */
 52398  		MemHashCursorSeek,          /* xSeek */
 52399  		MemHashCursorFirst,         /* xFirst */
 52400  		MemHashCursorLast,          /* xLast */
 52401  		MemHashCursorValid,         /* xValid */
 52402  		MemHashCursorNext,          /* xNext */
 52403  		MemHashCursorPrev,          /* xPrev */
 52404  		MemHashCursorDelete,        /* xDelete */
 52405  		MemHashCursorKeyLength,     /* xKeyLength */
 52406  		MemHashCursorKey,           /* xKey */
 52407  		MemHashCursorDataLength,    /* xDataLength */
 52408  		MemHashCursorData,          /* xData */
 52409  		MemHashCursorReset,         /* xReset */
 52410  		0        /* xRelease */                        
 52411  	};
 52412  	return &sMemStore;
 52413  }
 52414  /*
 52415   * ----------------------------------------------------------
 52416   * File: os.c
 52417   * MD5: e7ad243c3cd9e6aac5fba406eedb7766
 52418   * ----------------------------------------------------------
 52419   */
 52420  /*
 52421   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 52422   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 52423   * Version 1.1.6
 52424   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 52425   * please contact Symisc Systems via:
 52426   *       legal@symisc.net
 52427   *       licensing@symisc.net
 52428   *       contact@symisc.net
 52429   * or visit:
 52430   *      http://unqlite.org/licensing.html
 52431   */
 52432   /* $SymiscID: os.c v1.0 FreeBSD 2012-11-12 21:27 devel <chm@symisc.net> $ */
 52433  #ifndef UNQLITE_AMALGAMATION
 52434  #include "unqliteInt.h"
 52435  #endif
 52436  /* OS interfaces abstraction layers: Mostly SQLite3 source tree */
 52437  /*
 52438  ** The following routines are convenience wrappers around methods
 52439  ** of the unqlite_file object.  This is mostly just syntactic sugar. All
 52440  ** of this would be completely automatic if UnQLite were coded using
 52441  ** C++ instead of plain old C.
 52442  */
 52443  UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
 52444  {
 52445    return id->pMethods->xRead(id, pBuf, amt, offset);
 52446  }
 52447  UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
 52448  {
 52449    return id->pMethods->xWrite(id, pBuf, amt, offset);
 52450  }
 52451  UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size)
 52452  {
 52453    return id->pMethods->xTruncate(id, size);
 52454  }
 52455  UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags)
 52456  {
 52457    return id->pMethods->xSync(id, flags);
 52458  }
 52459  UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize)
 52460  {
 52461    return id->pMethods->xFileSize(id, pSize);
 52462  }
 52463  UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType)
 52464  {
 52465    return id->pMethods->xLock(id, lockType);
 52466  }
 52467  UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType)
 52468  {
 52469    return id->pMethods->xUnlock(id, lockType);
 52470  }
 52471  UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut)
 52472  {
 52473    return id->pMethods->xCheckReservedLock(id, pResOut);
 52474  }
 52475  UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id)
 52476  {
 52477    if( id->pMethods->xSectorSize ){
 52478  	  return id->pMethods->xSectorSize(id);
 52479    }
 52480    return  UNQLITE_DEFAULT_SECTOR_SIZE;
 52481  }
 52482  /*
 52483  ** The next group of routines are convenience wrappers around the
 52484  ** VFS methods.
 52485  */
 52486  UNQLITE_PRIVATE int unqliteOsOpen(
 52487    unqlite_vfs *pVfs,
 52488    SyMemBackend *pAlloc,
 52489    const char *zPath, 
 52490    unqlite_file **ppOut, 
 52491    unsigned int flags 
 52492  )
 52493  {
 52494  	unqlite_file *pFile;
 52495  	int rc;
 52496  	*ppOut = 0;
 52497  	if( zPath == 0 ){
 52498  		/* May happen if dealing with an in-memory database */
 52499  		return SXERR_EMPTY;
 52500  	}
 52501  	/* Allocate a new instance */
 52502  	pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile);
 52503  	if( pFile == 0 ){
 52504  		return UNQLITE_NOMEM;
 52505  	}
 52506  	/* Zero the structure */
 52507  	SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile);
 52508  	/* Invoke the xOpen method of the underlying VFS */
 52509  	rc = pVfs->xOpen(pVfs, zPath, pFile, flags);
 52510  	if( rc != UNQLITE_OK ){
 52511  		SyMemBackendFree(pAlloc,pFile);
 52512  		pFile = 0;
 52513  	}
 52514  	*ppOut = pFile;
 52515  	return rc;
 52516  }
 52517  UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId)
 52518  {
 52519  	int rc = UNQLITE_OK;
 52520  	if( pId ){
 52521  		rc = pId->pMethods->xClose(pId);
 52522  		SyMemBackendFree(pAlloc,pId);
 52523  	}
 52524  	return rc;
 52525  }
 52526  UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync){
 52527    return pVfs->xDelete(pVfs, zPath, dirSync);
 52528  }
 52529  UNQLITE_PRIVATE int unqliteOsAccess(
 52530    unqlite_vfs *pVfs, 
 52531    const char *zPath, 
 52532    int flags, 
 52533    int *pResOut
 52534  ){
 52535    return pVfs->xAccess(pVfs, zPath, flags, pResOut);
 52536  }
 52537  /*
 52538   * ----------------------------------------------------------
 52539   * File: os_unix.c
 52540   * MD5: 5efd57d03f8fb988d081c5bcf5cc2998
 52541   * ----------------------------------------------------------
 52542   */
 52543  /*
 52544   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 52545   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 52546   * Version 1.1.6
 52547   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 52548   * please contact Symisc Systems via:
 52549   *       legal@symisc.net
 52550   *       licensing@symisc.net
 52551   *       contact@symisc.net
 52552   * or visit:
 52553   *      http://unqlite.org/licensing.html
 52554   */
 52555   /* $SymiscID: os_unix.c v1.3 FreeBSD 2013-04-05 01:10 devel <chm@symisc.net> $ */
 52556  #ifndef UNQLITE_AMALGAMATION
 52557  #include "unqliteInt.h"
 52558  #endif
 52559  /* 
 52560   * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.).
 52561   * Note: Mostly SQLite3 source tree.
 52562   */
 52563  #if defined(__UNIXES__)
 52564  /** This file contains the VFS implementation for unix-like operating systems
 52565  ** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others.
 52566  **
 52567  ** There are actually several different VFS implementations in this file.
 52568  ** The differences are in the way that file locking is done.  The default
 52569  ** implementation uses Posix Advisory Locks.  Alternative implementations
 52570  ** use flock(), dot-files, various proprietary locking schemas, or simply
 52571  ** skip locking all together.
 52572  **
 52573  ** This source file is organized into divisions where the logic for various
 52574  ** subfunctions is contained within the appropriate division.  PLEASE
 52575  ** KEEP THE STRUCTURE OF THIS FILE INTACT.  New code should be placed
 52576  ** in the correct division and should be clearly labeled.
 52577  **
 52578  */
 52579  /*
 52580  ** standard include files.
 52581  */
 52582  #include <sys/types.h>
 52583  #include <sys/stat.h>
 52584  #include <sys/uio.h>
 52585  #include <sys/file.h>
 52586  #include <fcntl.h>
 52587  #include <unistd.h>
 52588  #include <time.h>
 52589  #include <sys/time.h>
 52590  #include <errno.h>
 52591  #if defined(__APPLE__) 
 52592  # include <sys/mount.h>
 52593  #endif
 52594  /*
 52595  ** Allowed values of unixFile.fsFlags
 52596  */
 52597  #define UNQLITE_FSFLAGS_IS_MSDOS     0x1
 52598  
 52599  /*
 52600  ** Default permissions when creating a new file
 52601  */
 52602  #ifndef UNQLITE_DEFAULT_FILE_PERMISSIONS
 52603  # define UNQLITE_DEFAULT_FILE_PERMISSIONS 0644
 52604  #endif
 52605  /*
 52606   ** Default permissions when creating auto proxy dir
 52607   */
 52608  #ifndef UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS
 52609  # define UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
 52610  #endif
 52611  /*
 52612  ** Maximum supported path-length.
 52613  */
 52614  #define MAX_PATHNAME 512
 52615  /*
 52616  ** Only set the lastErrno if the error code is a real error and not 
 52617  ** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK
 52618  */
 52619  #define IS_LOCK_ERROR(x)  ((x != UNQLITE_OK) && (x != UNQLITE_BUSY))
 52620  /* Forward references */
 52621  typedef struct unixInodeInfo unixInodeInfo;   /* An i-node */
 52622  typedef struct UnixUnusedFd UnixUnusedFd;     /* An unused file descriptor */
 52623  /*
 52624  ** Sometimes, after a file handle is closed by SQLite, the file descriptor
 52625  ** cannot be closed immediately. In these cases, instances of the following
 52626  ** structure are used to store the file descriptor while waiting for an
 52627  ** opportunity to either close or reuse it.
 52628  */
 52629  struct UnixUnusedFd {
 52630    int fd;                   /* File descriptor to close */
 52631    int flags;                /* Flags this file descriptor was opened with */
 52632    UnixUnusedFd *pNext;      /* Next unused file descriptor on same file */
 52633  };
 52634  /*
 52635  ** The unixFile structure is subclass of unqlite3_file specific to the unix
 52636  ** VFS implementations.
 52637  */
 52638  typedef struct unixFile unixFile;
 52639  struct unixFile {
 52640    const unqlite_io_methods *pMethod;  /* Always the first entry */
 52641    unixInodeInfo *pInode;              /* Info about locks on this inode */
 52642    int h;                              /* The file descriptor */
 52643    int dirfd;                          /* File descriptor for the directory */
 52644    unsigned char eFileLock;            /* The type of lock held on this fd */
 52645    int lastErrno;                      /* The unix errno from last I/O error */
 52646    void *lockingContext;               /* Locking style specific state */
 52647    UnixUnusedFd *pUnused;              /* Pre-allocated UnixUnusedFd */
 52648    int fileFlags;                      /* Miscellanous flags */
 52649    const char *zPath;                  /* Name of the file */
 52650    unsigned fsFlags;                   /* cached details from statfs() */
 52651  };
 52652  /*
 52653  ** The following macros define bits in unixFile.fileFlags
 52654  */
 52655  #define UNQLITE_WHOLE_FILE_LOCKING  0x0001   /* Use whole-file locking */
 52656  /*
 52657  ** Define various macros that are missing from some systems.
 52658  */
 52659  #ifndef O_LARGEFILE
 52660  # define O_LARGEFILE 0
 52661  #endif
 52662  #ifndef O_NOFOLLOW
 52663  # define O_NOFOLLOW 0
 52664  #endif
 52665  #ifndef O_BINARY
 52666  # define O_BINARY 0
 52667  #endif
 52668  /*
 52669  ** Helper functions to obtain and relinquish the global mutex. The
 52670  ** global mutex is used to protect the unixInodeInfo and
 52671  ** vxworksFileId objects used by this file, all of which may be 
 52672  ** shared by multiple threads.
 52673  **
 52674  ** Function unixMutexHeld() is used to assert() that the global mutex 
 52675  ** is held when required. This function is only used as part of assert() 
 52676  ** statements. e.g.
 52677  **
 52678  **   unixEnterMutex()
 52679  **     assert( unixMutexHeld() );
 52680  **   unixEnterLeave()
 52681  */
 52682  static void unixEnterMutex(void){
 52683  #ifdef UNQLITE_ENABLE_THREADS
 52684  	const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
 52685  	if( pMutexMethods ){
 52686  		SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
 52687  		SyMutexEnter(pMutexMethods,pMutex);
 52688  	}
 52689  #endif /* UNQLITE_ENABLE_THREADS */
 52690  }
 52691  static void unixLeaveMutex(void){
 52692  #ifdef UNQLITE_ENABLE_THREADS
 52693    const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
 52694    if( pMutexMethods ){
 52695  	 SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
 52696  	 SyMutexLeave(pMutexMethods,pMutex);
 52697    }
 52698  #endif /* UNQLITE_ENABLE_THREADS */
 52699  }
 52700  /*
 52701  ** This routine translates a standard POSIX errno code into something
 52702  ** useful to the clients of the unqlite3 functions.  Specifically, it is
 52703  ** intended to translate a variety of "try again" errors into UNQLITE_BUSY
 52704  ** and a variety of "please close the file descriptor NOW" errors into 
 52705  ** UNQLITE_IOERR
 52706  ** 
 52707  ** Errors during initialization of locks, or file system support for locks,
 52708  ** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
 52709  */
 52710  static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) {
 52711    switch (posixError) {
 52712    case 0: 
 52713      return UNQLITE_OK;
 52714      
 52715    case EAGAIN:
 52716    case ETIMEDOUT:
 52717    case EBUSY:
 52718    case EINTR:
 52719    case ENOLCK:  
 52720      /* random NFS retry error, unless during file system support 
 52721       * introspection, in which it actually means what it says */
 52722      return UNQLITE_BUSY;
 52723   
 52724    case EACCES: 
 52725      /* EACCES is like EAGAIN during locking operations, but not any other time*/
 52726        return UNQLITE_BUSY;
 52727      
 52728    case EPERM: 
 52729      return UNQLITE_PERM;
 52730      
 52731    case EDEADLK:
 52732      return UNQLITE_IOERR;
 52733      
 52734  #if EOPNOTSUPP!=ENOTSUP
 52735    case EOPNOTSUPP: 
 52736      /* something went terribly awry, unless during file system support 
 52737       * introspection, in which it actually means what it says */
 52738  #endif
 52739  #ifdef ENOTSUP
 52740    case ENOTSUP: 
 52741      /* invalid fd, unless during file system support introspection, in which 
 52742       * it actually means what it says */
 52743  #endif
 52744    case EIO:
 52745    case EBADF:
 52746    case EINVAL:
 52747    case ENOTCONN:
 52748    case ENODEV:
 52749    case ENXIO:
 52750    case ENOENT:
 52751    case ESTALE:
 52752    case ENOSYS:
 52753      /* these should force the client to close the file and reconnect */
 52754      
 52755    default: 
 52756      return unqliteIOErr;
 52757    }
 52758  }
 52759  /******************************************************************************
 52760  *************************** Posix Advisory Locking ****************************
 52761  **
 52762  ** POSIX advisory locks are broken by design.  ANSI STD 1003.1 (1996)
 52763  ** section 6.5.2.2 lines 483 through 490 specify that when a process
 52764  ** sets or clears a lock, that operation overrides any prior locks set
 52765  ** by the same process.  It does not explicitly say so, but this implies
 52766  ** that it overrides locks set by the same process using a different
 52767  ** file descriptor.  Consider this test case:
 52768  **
 52769  **       int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
 52770  **       int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
 52771  **
 52772  ** Suppose ./file1 and ./file2 are really the same file (because
 52773  ** one is a hard or symbolic link to the other) then if you set
 52774  ** an exclusive lock on fd1, then try to get an exclusive lock
 52775  ** on fd2, it works.  I would have expected the second lock to
 52776  ** fail since there was already a lock on the file due to fd1.
 52777  ** But not so.  Since both locks came from the same process, the
 52778  ** second overrides the first, even though they were on different
 52779  ** file descriptors opened on different file names.
 52780  **
 52781  ** This means that we cannot use POSIX locks to synchronize file access
 52782  ** among competing threads of the same process.  POSIX locks will work fine
 52783  ** to synchronize access for threads in separate processes, but not
 52784  ** threads within the same process.
 52785  **
 52786  ** To work around the problem, SQLite has to manage file locks internally
 52787  ** on its own.  Whenever a new database is opened, we have to find the
 52788  ** specific inode of the database file (the inode is determined by the
 52789  ** st_dev and st_ino fields of the stat structure that fstat() fills in)
 52790  ** and check for locks already existing on that inode.  When locks are
 52791  ** created or removed, we have to look at our own internal record of the
 52792  ** locks to see if another thread has previously set a lock on that same
 52793  ** inode.
 52794  **
 52795  ** (Aside: The use of inode numbers as unique IDs does not work on VxWorks.
 52796  ** For VxWorks, we have to use the alternative unique ID system based on
 52797  ** canonical filename and implemented in the previous division.)
 52798  **
 52799  ** There is one locking structure
 52800  ** per inode, so if the same inode is opened twice, both unixFile structures
 52801  ** point to the same locking structure.  The locking structure keeps
 52802  ** a reference count (so we will know when to delete it) and a "cnt"
 52803  ** field that tells us its internal lock status.  cnt==0 means the
 52804  ** file is unlocked.  cnt==-1 means the file has an exclusive lock.
 52805  ** cnt>0 means there are cnt shared locks on the file.
 52806  **
 52807  ** Any attempt to lock or unlock a file first checks the locking
 52808  ** structure.  The fcntl() system call is only invoked to set a 
 52809  ** POSIX lock if the internal lock structure transitions between
 52810  ** a locked and an unlocked state.
 52811  **
 52812  ** But wait:  there are yet more problems with POSIX advisory locks.
 52813  **
 52814  ** If you close a file descriptor that points to a file that has locks,
 52815  ** all locks on that file that are owned by the current process are
 52816  ** released.  To work around this problem, each unixInodeInfo object
 52817  ** maintains a count of the number of pending locks on that inode.
 52818  ** When an attempt is made to close an unixFile, if there are
 52819  ** other unixFile open on the same inode that are holding locks, the call
 52820  ** to close() the file descriptor is deferred until all of the locks clear.
 52821  ** The unixInodeInfo structure keeps a list of file descriptors that need to
 52822  ** be closed and that list is walked (and cleared) when the last lock
 52823  ** clears.
 52824  **
 52825  ** Yet another problem:  LinuxThreads do not play well with posix locks.
 52826  **
 52827  ** Many older versions of linux use the LinuxThreads library which is
 52828  ** not posix compliant.  Under LinuxThreads, a lock created by thread
 52829  ** A cannot be modified or overridden by a different thread B.
 52830  ** Only thread A can modify the lock.  Locking behavior is correct
 52831  ** if the appliation uses the newer Native Posix Thread Library (NPTL)
 52832  ** on linux - with NPTL a lock created by thread A can override locks
 52833  ** in thread B.  But there is no way to know at compile-time which
 52834  ** threading library is being used.  So there is no way to know at
 52835  ** compile-time whether or not thread A can override locks on thread B.
 52836  ** One has to do a run-time check to discover the behavior of the
 52837  ** current process.
 52838  **
 52839  */
 52840  
 52841  /*
 52842  ** An instance of the following structure serves as the key used
 52843  ** to locate a particular unixInodeInfo object.
 52844  */
 52845  struct unixFileId {
 52846    dev_t dev;                  /* Device number */
 52847    ino_t ino;                  /* Inode number */
 52848  };
 52849  /*
 52850  ** An instance of the following structure is allocated for each open
 52851  ** inode.  Or, on LinuxThreads, there is one of these structures for
 52852  ** each inode opened by each thread.
 52853  **
 52854  ** A single inode can have multiple file descriptors, so each unixFile
 52855  ** structure contains a pointer to an instance of this object and this
 52856  ** object keeps a count of the number of unixFile pointing to it.
 52857  */
 52858  struct unixInodeInfo {
 52859    struct unixFileId fileId;       /* The lookup key */
 52860    int nShared;                    /* Number of SHARED locks held */
 52861    int eFileLock;                  /* One of SHARED_LOCK, RESERVED_LOCK etc. */
 52862    int nRef;                       /* Number of pointers to this structure */
 52863    int nLock;                      /* Number of outstanding file locks */
 52864    UnixUnusedFd *pUnused;          /* Unused file descriptors to close */
 52865    unixInodeInfo *pNext;           /* List of all unixInodeInfo objects */
 52866    unixInodeInfo *pPrev;           /*    .... doubly linked */
 52867  };
 52868  
 52869  static unixInodeInfo *inodeList = 0;
 52870  /*
 52871   * Local memory allocation stuff.
 52872   */
 52873  static void * unqlite_malloc(sxu32 nByte)
 52874  {
 52875  	SyMemBackend *pAlloc;
 52876  	void *p;
 52877  	pAlloc = (SyMemBackend *)unqliteExportMemBackend();
 52878  	p = SyMemBackendAlloc(pAlloc,nByte);
 52879  	return p;
 52880  }
 52881  static void unqlite_free(void *p)
 52882  {
 52883  	SyMemBackend *pAlloc;
 52884  	pAlloc = (SyMemBackend *)unqliteExportMemBackend();
 52885  	SyMemBackendFree(pAlloc,p);
 52886  }
 52887  /*
 52888  ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
 52889  ** If all such file descriptors are closed without error, the list is
 52890  ** cleared and UNQLITE_OK returned.
 52891  **
 52892  ** Otherwise, if an error occurs, then successfully closed file descriptor
 52893  ** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned. 
 52894  ** not deleted and UNQLITE_IOERR_CLOSE returned.
 52895  */ 
 52896  static int closePendingFds(unixFile *pFile){
 52897    int rc = UNQLITE_OK;
 52898    unixInodeInfo *pInode = pFile->pInode;
 52899    UnixUnusedFd *pError = 0;
 52900    UnixUnusedFd *p;
 52901    UnixUnusedFd *pNext;
 52902    for(p=pInode->pUnused; p; p=pNext){
 52903      pNext = p->pNext;
 52904      if( close(p->fd) ){
 52905        pFile->lastErrno = errno;
 52906  	  rc = UNQLITE_IOERR;
 52907        p->pNext = pError;
 52908        pError = p;
 52909      }else{
 52910        unqlite_free(p);
 52911      }
 52912    }
 52913    pInode->pUnused = pError;
 52914    return rc;
 52915  }
 52916  /*
 52917  ** Release a unixInodeInfo structure previously allocated by findInodeInfo().
 52918  **
 52919  ** The mutex entered using the unixEnterMutex() function must be held
 52920  ** when this function is called.
 52921  */
 52922  static void releaseInodeInfo(unixFile *pFile){
 52923    unixInodeInfo *pInode = pFile->pInode;
 52924    if( pInode ){
 52925      pInode->nRef--;
 52926      if( pInode->nRef==0 ){
 52927        closePendingFds(pFile);
 52928        if( pInode->pPrev ){
 52929          pInode->pPrev->pNext = pInode->pNext;
 52930        }else{
 52931          inodeList = pInode->pNext;
 52932        }
 52933        if( pInode->pNext ){
 52934          pInode->pNext->pPrev = pInode->pPrev;
 52935        }
 52936        unqlite_free(pInode);
 52937      }
 52938    }
 52939  }
 52940  /*
 52941  ** Given a file descriptor, locate the unixInodeInfo object that
 52942  ** describes that file descriptor.  Create a new one if necessary.  The
 52943  ** return value might be uninitialized if an error occurs.
 52944  **
 52945  ** The mutex entered using the unixEnterMutex() function must be held
 52946  ** when this function is called.
 52947  **
 52948  ** Return an appropriate error code.
 52949  */
 52950  static int findInodeInfo(
 52951    unixFile *pFile,               /* Unix file with file desc used in the key */
 52952    unixInodeInfo **ppInode        /* Return the unixInodeInfo object here */
 52953  ){
 52954    int rc;                        /* System call return code */
 52955    int fd;                        /* The file descriptor for pFile */
 52956    struct unixFileId fileId;      /* Lookup key for the unixInodeInfo */
 52957    struct stat statbuf;           /* Low-level file information */
 52958    unixInodeInfo *pInode = 0;     /* Candidate unixInodeInfo object */
 52959  
 52960    /* Get low-level information about the file that we can used to
 52961    ** create a unique name for the file.
 52962    */
 52963    fd = pFile->h;
 52964    rc = fstat(fd, &statbuf);
 52965    if( rc!=0 ){
 52966      pFile->lastErrno = errno;
 52967  #ifdef EOVERFLOW
 52968  	if( pFile->lastErrno==EOVERFLOW ) return UNQLITE_NOTIMPLEMENTED;
 52969  #endif
 52970      return UNQLITE_IOERR;
 52971    }
 52972  
 52973  #ifdef __APPLE__
 52974    /* On OS X on an msdos filesystem, the inode number is reported
 52975    ** incorrectly for zero-size files.  See ticket #3260.  To work
 52976    ** around this problem (we consider it a bug in OS X, not SQLite)
 52977    ** we always increase the file size to 1 by writing a single byte
 52978    ** prior to accessing the inode number.  The one byte written is
 52979    ** an ASCII 'S' character which also happens to be the first byte
 52980    ** in the header of every SQLite database.  In this way, if there
 52981    ** is a race condition such that another thread has already populated
 52982    ** the first page of the database, no damage is done.
 52983    */
 52984    if( statbuf.st_size==0 && (pFile->fsFlags & UNQLITE_FSFLAGS_IS_MSDOS)!=0 ){
 52985      rc = write(fd, "S", 1);
 52986      if( rc!=1 ){
 52987        pFile->lastErrno = errno;
 52988        return UNQLITE_IOERR;
 52989      }
 52990      rc = fstat(fd, &statbuf);
 52991      if( rc!=0 ){
 52992        pFile->lastErrno = errno;
 52993        return UNQLITE_IOERR;
 52994      }
 52995    }
 52996  #endif
 52997    SyZero(&fileId,sizeof(fileId));
 52998    fileId.dev = statbuf.st_dev;
 52999    fileId.ino = statbuf.st_ino;
 53000    pInode = inodeList;
 53001    while( pInode && SyMemcmp((const void *)&fileId,(const void *)&pInode->fileId, sizeof(fileId)) ){
 53002      pInode = pInode->pNext;
 53003    }
 53004    if( pInode==0 ){
 53005      pInode = (unixInodeInfo *)unqlite_malloc( sizeof(*pInode) );
 53006      if( pInode==0 ){
 53007        return UNQLITE_NOMEM;
 53008      }
 53009      SyZero(pInode,sizeof(*pInode));
 53010  	SyMemcpy((const void *)&fileId,(void *)&pInode->fileId,sizeof(fileId));
 53011      pInode->nRef = 1;
 53012      pInode->pNext = inodeList;
 53013      pInode->pPrev = 0;
 53014      if( inodeList ) inodeList->pPrev = pInode;
 53015      inodeList = pInode;
 53016    }else{
 53017      pInode->nRef++;
 53018    }
 53019    *ppInode = pInode;
 53020    return UNQLITE_OK;
 53021  }
 53022  /*
 53023  ** This routine checks if there is a RESERVED lock held on the specified
 53024  ** file by this or any other process. If such a lock is held, set *pResOut
 53025  ** to a non-zero value otherwise *pResOut is set to zero.  The return value
 53026  ** is set to UNQLITE_OK unless an I/O error occurs during lock checking.
 53027  */
 53028  static int unixCheckReservedLock(unqlite_file *id, int *pResOut){
 53029    int rc = UNQLITE_OK;
 53030    int reserved = 0;
 53031    unixFile *pFile = (unixFile*)id;
 53032  
 53033   
 53034    unixEnterMutex(); /* Because pFile->pInode is shared across threads */
 53035  
 53036    /* Check if a thread in this process holds such a lock */
 53037    if( pFile->pInode->eFileLock>SHARED_LOCK ){
 53038      reserved = 1;
 53039    }
 53040  
 53041    /* Otherwise see if some other process holds it.
 53042    */
 53043    if( !reserved ){
 53044      struct flock lock;
 53045      lock.l_whence = SEEK_SET;
 53046      lock.l_start = RESERVED_BYTE;
 53047      lock.l_len = 1;
 53048      lock.l_type = F_WRLCK;
 53049      if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
 53050        int tErrno = errno;
 53051  	  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53052        pFile->lastErrno = tErrno;
 53053      } else if( lock.l_type!=F_UNLCK ){
 53054        reserved = 1;
 53055      }
 53056    }
 53057    
 53058    unixLeaveMutex();
 53059   
 53060    *pResOut = reserved;
 53061    return rc;
 53062  }
 53063  /*
 53064  ** Lock the file with the lock specified by parameter eFileLock - one
 53065  ** of the following:
 53066  **
 53067  **     (1) SHARED_LOCK
 53068  **     (2) RESERVED_LOCK
 53069  **     (3) PENDING_LOCK
 53070  **     (4) EXCLUSIVE_LOCK
 53071  **
 53072  ** Sometimes when requesting one lock state, additional lock states
 53073  ** are inserted in between.  The locking might fail on one of the later
 53074  ** transitions leaving the lock state different from what it started but
 53075  ** still short of its goal.  The following chart shows the allowed
 53076  ** transitions and the inserted intermediate states:
 53077  **
 53078  **    UNLOCKED -> SHARED
 53079  **    SHARED -> RESERVED
 53080  **    SHARED -> (PENDING) -> EXCLUSIVE
 53081  **    RESERVED -> (PENDING) -> EXCLUSIVE
 53082  **    PENDING -> EXCLUSIVE
 53083  **
 53084  ** This routine will only increase a lock.  Use the unqliteOsUnlock()
 53085  ** routine to lower a locking level.
 53086  */
 53087  static int unixLock(unqlite_file *id, int eFileLock){
 53088    /* The following describes the implementation of the various locks and
 53089    ** lock transitions in terms of the POSIX advisory shared and exclusive
 53090    ** lock primitives (called read-locks and write-locks below, to avoid
 53091    ** confusion with SQLite lock names). The algorithms are complicated
 53092    ** slightly in order to be compatible with unixdows systems simultaneously
 53093    ** accessing the same database file, in case that is ever required.
 53094    **
 53095    ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
 53096    ** byte', each single bytes at well known offsets, and the 'shared byte
 53097    ** range', a range of 510 bytes at a well known offset.
 53098    **
 53099    ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
 53100    ** byte'.  If this is successful, a random byte from the 'shared byte
 53101    ** range' is read-locked and the lock on the 'pending byte' released.
 53102    **
 53103    ** A process may only obtain a RESERVED lock after it has a SHARED lock.
 53104    ** A RESERVED lock is implemented by grabbing a write-lock on the
 53105    ** 'reserved byte'. 
 53106    **
 53107    ** A process may only obtain a PENDING lock after it has obtained a
 53108    ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
 53109    ** on the 'pending byte'. This ensures that no new SHARED locks can be
 53110    ** obtained, but existing SHARED locks are allowed to persist. A process
 53111    ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
 53112    ** This property is used by the algorithm for rolling back a journal file
 53113    ** after a crash.
 53114    **
 53115    ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
 53116    ** implemented by obtaining a write-lock on the entire 'shared byte
 53117    ** range'. Since all other locks require a read-lock on one of the bytes
 53118    ** within this range, this ensures that no other locks are held on the
 53119    ** database. 
 53120    **
 53121    ** The reason a single byte cannot be used instead of the 'shared byte
 53122    ** range' is that some versions of unixdows do not support read-locks. By
 53123    ** locking a random byte from a range, concurrent SHARED locks may exist
 53124    ** even if the locking primitive used is always a write-lock.
 53125    */
 53126    int rc = UNQLITE_OK;
 53127    unixFile *pFile = (unixFile*)id;
 53128    unixInodeInfo *pInode = pFile->pInode;
 53129    struct flock lock;
 53130    int s = 0;
 53131    int tErrno = 0;
 53132  
 53133    /* If there is already a lock of this type or more restrictive on the
 53134    ** unixFile, do nothing. Don't use the end_lock: exit path, as
 53135    ** unixEnterMutex() hasn't been called yet.
 53136    */
 53137    if( pFile->eFileLock>=eFileLock ){
 53138      return UNQLITE_OK;
 53139    }
 53140    /* This mutex is needed because pFile->pInode is shared across threads
 53141    */
 53142    unixEnterMutex();
 53143    pInode = pFile->pInode;
 53144  
 53145    /* If some thread using this PID has a lock via a different unixFile*
 53146    ** handle that precludes the requested lock, return BUSY.
 53147    */
 53148    if( (pFile->eFileLock!=pInode->eFileLock && 
 53149            (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
 53150    ){
 53151      rc = UNQLITE_BUSY;
 53152      goto end_lock;
 53153    }
 53154  
 53155    /* If a SHARED lock is requested, and some thread using this PID already
 53156    ** has a SHARED or RESERVED lock, then increment reference counts and
 53157    ** return UNQLITE_OK.
 53158    */
 53159    if( eFileLock==SHARED_LOCK && 
 53160        (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
 53161      pFile->eFileLock = SHARED_LOCK;
 53162      pInode->nShared++;
 53163      pInode->nLock++;
 53164      goto end_lock;
 53165    }
 53166    /* A PENDING lock is needed before acquiring a SHARED lock and before
 53167    ** acquiring an EXCLUSIVE lock.  For the SHARED lock, the PENDING will
 53168    ** be released.
 53169    */
 53170    lock.l_len = 1L;
 53171    lock.l_whence = SEEK_SET;
 53172    if( eFileLock==SHARED_LOCK 
 53173        || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
 53174    ){
 53175      lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
 53176      lock.l_start = PENDING_BYTE;
 53177      s = fcntl(pFile->h, F_SETLK, &lock);
 53178      if( s==(-1) ){
 53179        tErrno = errno;
 53180        rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53181        if( IS_LOCK_ERROR(rc) ){
 53182          pFile->lastErrno = tErrno;
 53183        }
 53184        goto end_lock;
 53185      }
 53186    }
 53187    /* If control gets to this point, then actually go ahead and make
 53188    ** operating system calls for the specified lock.
 53189    */
 53190    if( eFileLock==SHARED_LOCK ){
 53191      /* Now get the read-lock */
 53192      lock.l_start = SHARED_FIRST;
 53193      lock.l_len = SHARED_SIZE;
 53194      if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
 53195        tErrno = errno;
 53196      }
 53197      /* Drop the temporary PENDING lock */
 53198      lock.l_start = PENDING_BYTE;
 53199      lock.l_len = 1L;
 53200      lock.l_type = F_UNLCK;
 53201      if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
 53202        if( s != -1 ){
 53203          /* This could happen with a network mount */
 53204          tErrno = errno; 
 53205          rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); 
 53206          if( IS_LOCK_ERROR(rc) ){
 53207            pFile->lastErrno = tErrno;
 53208          }
 53209          goto end_lock;
 53210        }
 53211      }
 53212      if( s==(-1) ){
 53213  		rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53214        if( IS_LOCK_ERROR(rc) ){
 53215          pFile->lastErrno = tErrno;
 53216        }
 53217      }else{
 53218        pFile->eFileLock = SHARED_LOCK;
 53219        pInode->nLock++;
 53220        pInode->nShared = 1;
 53221      }
 53222    }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
 53223      /* We are trying for an exclusive lock but another thread in this
 53224      ** same process is still holding a shared lock. */
 53225      rc = UNQLITE_BUSY;
 53226    }else{
 53227      /* The request was for a RESERVED or EXCLUSIVE lock.  It is
 53228      ** assumed that there is a SHARED or greater lock on the file
 53229      ** already.
 53230      */
 53231      lock.l_type = F_WRLCK;
 53232      switch( eFileLock ){
 53233        case RESERVED_LOCK:
 53234          lock.l_start = RESERVED_BYTE;
 53235          break;
 53236        case EXCLUSIVE_LOCK:
 53237          lock.l_start = SHARED_FIRST;
 53238          lock.l_len = SHARED_SIZE;
 53239          break;
 53240        default:
 53241  		  /* Can't happen */
 53242          break;
 53243      }
 53244      s = fcntl(pFile->h, F_SETLK, &lock);
 53245      if( s==(-1) ){
 53246        tErrno = errno;
 53247        rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53248        if( IS_LOCK_ERROR(rc) ){
 53249          pFile->lastErrno = tErrno;
 53250        }
 53251      }
 53252    }
 53253    if( rc==UNQLITE_OK ){
 53254      pFile->eFileLock = eFileLock;
 53255      pInode->eFileLock = eFileLock;
 53256    }else if( eFileLock==EXCLUSIVE_LOCK ){
 53257      pFile->eFileLock = PENDING_LOCK;
 53258      pInode->eFileLock = PENDING_LOCK;
 53259    }
 53260  end_lock:
 53261    unixLeaveMutex();
 53262    return rc;
 53263  }
 53264  /*
 53265  ** Add the file descriptor used by file handle pFile to the corresponding
 53266  ** pUnused list.
 53267  */
 53268  static void setPendingFd(unixFile *pFile){
 53269    unixInodeInfo *pInode = pFile->pInode;
 53270    UnixUnusedFd *p = pFile->pUnused;
 53271    p->pNext = pInode->pUnused;
 53272    pInode->pUnused = p;
 53273    pFile->h = -1;
 53274    pFile->pUnused = 0;
 53275  }
 53276  /*
 53277  ** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
 53278  ** must be either NO_LOCK or SHARED_LOCK.
 53279  **
 53280  ** If the locking level of the file descriptor is already at or below
 53281  ** the requested locking level, this routine is a no-op.
 53282  ** 
 53283  ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
 53284  ** the byte range is divided into 2 parts and the first part is unlocked then
 53285  ** set to a read lock, then the other part is simply unlocked.  This works 
 53286  ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to 
 53287  ** remove the write lock on a region when a read lock is set.
 53288  */
 53289  static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){
 53290    unixFile *pFile = (unixFile*)id;
 53291    unixInodeInfo *pInode;
 53292    struct flock lock;
 53293    int rc = UNQLITE_OK;
 53294    int h;
 53295    int tErrno;                      /* Error code from system call errors */
 53296  
 53297     if( pFile->eFileLock<=eFileLock ){
 53298      return UNQLITE_OK;
 53299    }
 53300    unixEnterMutex();
 53301    
 53302    h = pFile->h;
 53303    pInode = pFile->pInode;
 53304    
 53305    if( pFile->eFileLock>SHARED_LOCK ){
 53306      /* downgrading to a shared lock on NFS involves clearing the write lock
 53307      ** before establishing the readlock - to avoid a race condition we downgrade
 53308      ** the lock in 2 blocks, so that part of the range will be covered by a 
 53309      ** write lock until the rest is covered by a read lock:
 53310      **  1:   [WWWWW]
 53311      **  2:   [....W]
 53312      **  3:   [RRRRW]
 53313      **  4:   [RRRR.]
 53314      */
 53315      if( eFileLock==SHARED_LOCK ){
 53316        if( handleNFSUnlock ){
 53317          off_t divSize = SHARED_SIZE - 1;
 53318          
 53319          lock.l_type = F_UNLCK;
 53320          lock.l_whence = SEEK_SET;
 53321          lock.l_start = SHARED_FIRST;
 53322          lock.l_len = divSize;
 53323          if( fcntl(h, F_SETLK, &lock)==(-1) ){
 53324            tErrno = errno;
 53325  		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53326            if( IS_LOCK_ERROR(rc) ){
 53327              pFile->lastErrno = tErrno;
 53328            }
 53329            goto end_unlock;
 53330          }
 53331          lock.l_type = F_RDLCK;
 53332          lock.l_whence = SEEK_SET;
 53333          lock.l_start = SHARED_FIRST;
 53334          lock.l_len = divSize;
 53335          if( fcntl(h, F_SETLK, &lock)==(-1) ){
 53336            tErrno = errno;
 53337  		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53338            if( IS_LOCK_ERROR(rc) ){
 53339              pFile->lastErrno = tErrno;
 53340            }
 53341            goto end_unlock;
 53342          }
 53343          lock.l_type = F_UNLCK;
 53344          lock.l_whence = SEEK_SET;
 53345          lock.l_start = SHARED_FIRST+divSize;
 53346          lock.l_len = SHARED_SIZE-divSize;
 53347          if( fcntl(h, F_SETLK, &lock)==(-1) ){
 53348            tErrno = errno;
 53349  		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53350            if( IS_LOCK_ERROR(rc) ){
 53351              pFile->lastErrno = tErrno;
 53352            }
 53353            goto end_unlock;
 53354          }
 53355        }else{
 53356          lock.l_type = F_RDLCK;
 53357          lock.l_whence = SEEK_SET;
 53358          lock.l_start = SHARED_FIRST;
 53359          lock.l_len = SHARED_SIZE;
 53360          if( fcntl(h, F_SETLK, &lock)==(-1) ){
 53361            tErrno = errno;
 53362  		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53363            if( IS_LOCK_ERROR(rc) ){
 53364              pFile->lastErrno = tErrno;
 53365            }
 53366            goto end_unlock;
 53367          }
 53368        }
 53369      }
 53370      lock.l_type = F_UNLCK;
 53371      lock.l_whence = SEEK_SET;
 53372      lock.l_start = PENDING_BYTE;
 53373      lock.l_len = 2L;
 53374      if( fcntl(h, F_SETLK, &lock)!=(-1) ){
 53375        pInode->eFileLock = SHARED_LOCK;
 53376      }else{
 53377        tErrno = errno;
 53378  	  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53379        if( IS_LOCK_ERROR(rc) ){
 53380          pFile->lastErrno = tErrno;
 53381        }
 53382        goto end_unlock;
 53383      }
 53384    }
 53385    if( eFileLock==NO_LOCK ){
 53386      /* Decrement the shared lock counter.  Release the lock using an
 53387      ** OS call only when all threads in this same process have released
 53388      ** the lock.
 53389      */
 53390      pInode->nShared--;
 53391      if( pInode->nShared==0 ){
 53392        lock.l_type = F_UNLCK;
 53393        lock.l_whence = SEEK_SET;
 53394        lock.l_start = lock.l_len = 0L;
 53395        
 53396        if( fcntl(h, F_SETLK, &lock)!=(-1) ){
 53397          pInode->eFileLock = NO_LOCK;
 53398        }else{
 53399          tErrno = errno;
 53400  		rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
 53401          if( IS_LOCK_ERROR(rc) ){
 53402            pFile->lastErrno = tErrno;
 53403          }
 53404          pInode->eFileLock = NO_LOCK;
 53405          pFile->eFileLock = NO_LOCK;
 53406        }
 53407      }
 53408  
 53409      /* Decrement the count of locks against this same file.  When the
 53410      ** count reaches zero, close any other file descriptors whose close
 53411      ** was deferred because of outstanding locks.
 53412      */
 53413      pInode->nLock--;
 53414   
 53415      if( pInode->nLock==0 ){
 53416        int rc2 = closePendingFds(pFile);
 53417        if( rc==UNQLITE_OK ){
 53418          rc = rc2;
 53419        }
 53420      }
 53421    }
 53422  	
 53423  end_unlock:
 53424  
 53425    unixLeaveMutex();
 53426    
 53427    if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock;
 53428    return rc;
 53429  }
 53430  /*
 53431  ** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
 53432  ** must be either NO_LOCK or SHARED_LOCK.
 53433  **
 53434  ** If the locking level of the file descriptor is already at or below
 53435  ** the requested locking level, this routine is a no-op.
 53436  */
 53437  static int unixUnlock(unqlite_file *id, int eFileLock){
 53438    return _posixUnlock(id, eFileLock, 0);
 53439  }
 53440  /*
 53441  ** This function performs the parts of the "close file" operation 
 53442  ** common to all locking schemes. It closes the directory and file
 53443  ** handles, if they are valid, and sets all fields of the unixFile
 53444  ** structure to 0.
 53445  **
 53446  */
 53447  static int closeUnixFile(unqlite_file *id){
 53448    unixFile *pFile = (unixFile*)id;
 53449    if( pFile ){
 53450      if( pFile->dirfd>=0 ){
 53451        int err = close(pFile->dirfd);
 53452        if( err ){
 53453          pFile->lastErrno = errno;
 53454          return UNQLITE_IOERR;
 53455        }else{
 53456          pFile->dirfd=-1;
 53457        }
 53458      }
 53459      if( pFile->h>=0 ){
 53460        int err = close(pFile->h);
 53461        if( err ){
 53462          pFile->lastErrno = errno;
 53463          return UNQLITE_IOERR;
 53464        }
 53465      }
 53466      unqlite_free(pFile->pUnused);
 53467      SyZero(pFile,sizeof(unixFile));
 53468    }
 53469    return UNQLITE_OK;
 53470  }
 53471  /*
 53472  ** Close a file.
 53473  */
 53474  static int unixClose(unqlite_file *id){
 53475    int rc = UNQLITE_OK;
 53476    if( id ){
 53477      unixFile *pFile = (unixFile *)id;
 53478      unixUnlock(id, NO_LOCK);
 53479      unixEnterMutex();
 53480      if( pFile->pInode && pFile->pInode->nLock ){
 53481        /* If there are outstanding locks, do not actually close the file just
 53482        ** yet because that would clear those locks.  Instead, add the file
 53483        ** descriptor to pInode->pUnused list.  It will be automatically closed 
 53484        ** when the last lock is cleared.
 53485        */
 53486        setPendingFd(pFile);
 53487      }
 53488      releaseInodeInfo(pFile);
 53489      rc = closeUnixFile(id);
 53490      unixLeaveMutex();
 53491    }
 53492    return rc;
 53493  }
 53494  /************** End of the posix advisory lock implementation *****************
 53495  ******************************************************************************/
 53496  /*
 53497  **
 53498  ** The next division contains implementations for all methods of the 
 53499  ** unqlite_file object other than the locking methods.  The locking
 53500  ** methods were defined in divisions above (one locking method per
 53501  ** division).  Those methods that are common to all locking modes
 53502  ** are gather together into this division.
 53503  */
 53504  /*
 53505  ** Seek to the offset passed as the second argument, then read cnt 
 53506  ** bytes into pBuf. Return the number of bytes actually read.
 53507  **
 53508  ** NB:  If you define USE_PREAD or USE_PREAD64, then it might also
 53509  ** be necessary to define _XOPEN_SOURCE to be 500.  This varies from
 53510  ** one system to another.  Since SQLite does not define USE_PREAD
 53511  ** any form by default, we will not attempt to define _XOPEN_SOURCE.
 53512  ** See tickets #2741 and #2681.
 53513  **
 53514  ** To avoid stomping the errno value on a failed read the lastErrno value
 53515  ** is set before returning.
 53516  */
 53517  static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){
 53518    int got;
 53519  #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
 53520    unqlite_int64 newOffset;
 53521  #endif
 53522   
 53523  #if defined(USE_PREAD)
 53524    got = pread(id->h, pBuf, cnt, offset);
 53525  #elif defined(USE_PREAD64)
 53526    got = pread64(id->h, pBuf, cnt, offset);
 53527  #else
 53528    newOffset = lseek(id->h, offset, SEEK_SET);
 53529    
 53530    if( newOffset!=offset ){
 53531      if( newOffset == -1 ){
 53532        ((unixFile*)id)->lastErrno = errno;
 53533      }else{
 53534        ((unixFile*)id)->lastErrno = 0;			
 53535      }
 53536      return -1;
 53537    }
 53538    got = read(id->h, pBuf, cnt);
 53539  #endif
 53540    if( got<0 ){
 53541      ((unixFile*)id)->lastErrno = errno;
 53542    }
 53543    return got;
 53544  }
 53545  /*
 53546  ** Read data from a file into a buffer.  Return UNQLITE_OK if all
 53547  ** bytes were read successfully and UNQLITE_IOERR if anything goes
 53548  ** wrong.
 53549  */
 53550  static int unixRead(
 53551    unqlite_file *id, 
 53552    void *pBuf, 
 53553    unqlite_int64 amt,
 53554    unqlite_int64 offset
 53555  ){
 53556    unixFile *pFile = (unixFile *)id;
 53557    int got;
 53558    
 53559    got = seekAndRead(pFile, offset, pBuf, (int)amt);
 53560    if( got==(int)amt ){
 53561      return UNQLITE_OK;
 53562    }else if( got<0 ){
 53563      /* lastErrno set by seekAndRead */
 53564      return UNQLITE_IOERR;
 53565    }else{
 53566      pFile->lastErrno = 0; /* not a system error */
 53567      /* Unread parts of the buffer must be zero-filled */
 53568      SyZero(&((char*)pBuf)[got],(sxu32)amt-got);
 53569      return UNQLITE_IOERR;
 53570    }
 53571  }
 53572  /*
 53573  ** Seek to the offset in id->offset then read cnt bytes into pBuf.
 53574  ** Return the number of bytes actually read.  Update the offset.
 53575  **
 53576  ** To avoid stomping the errno value on a failed write the lastErrno value
 53577  ** is set before returning.
 53578  */
 53579  static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, unqlite_int64 cnt){
 53580    int got;
 53581  #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
 53582    unqlite_int64 newOffset;
 53583  #endif
 53584    
 53585  #if defined(USE_PREAD)
 53586    got = pwrite(id->h, pBuf, cnt, offset);
 53587  #elif defined(USE_PREAD64)
 53588    got = pwrite64(id->h, pBuf, cnt, offset);
 53589  #else
 53590    newOffset = lseek(id->h, offset, SEEK_SET);
 53591    if( newOffset!=offset ){
 53592      if( newOffset == -1 ){
 53593        ((unixFile*)id)->lastErrno = errno;
 53594      }else{
 53595        ((unixFile*)id)->lastErrno = 0;			
 53596      }
 53597      return -1;
 53598    }
 53599    got = write(id->h, pBuf, cnt);
 53600  #endif
 53601    if( got<0 ){
 53602      ((unixFile*)id)->lastErrno = errno;
 53603    }
 53604    return got;
 53605  }
 53606  /*
 53607  ** Write data from a buffer into a file.  Return UNQLITE_OK on success
 53608  ** or some other error code on failure.
 53609  */
 53610  static int unixWrite(
 53611    unqlite_file *id, 
 53612    const void *pBuf, 
 53613    unqlite_int64 amt,
 53614    unqlite_int64 offset 
 53615  ){
 53616    unixFile *pFile = (unixFile*)id;
 53617    int wrote = 0;
 53618  
 53619    while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
 53620      amt -= wrote;
 53621      offset += wrote;
 53622      pBuf = &((char*)pBuf)[wrote];
 53623    }
 53624    
 53625    if( amt>0 ){
 53626      if( wrote<0 ){
 53627        /* lastErrno set by seekAndWrite */
 53628        return UNQLITE_IOERR;
 53629      }else{
 53630        pFile->lastErrno = 0; /* not a system error */
 53631        return UNQLITE_FULL;
 53632      }
 53633    }
 53634    return UNQLITE_OK;
 53635  }
 53636  /*
 53637  ** We do not trust systems to provide a working fdatasync().  Some do.
 53638  ** Others do no.  To be safe, we will stick with the (slower) fsync().
 53639  ** If you know that your system does support fdatasync() correctly,
 53640  ** then simply compile with -Dfdatasync=fdatasync
 53641  */
 53642  #if !defined(fdatasync) && !defined(__linux__)
 53643  # define fdatasync fsync
 53644  #endif
 53645  
 53646  /*
 53647  ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
 53648  ** the F_FULLFSYNC macro is defined.  F_FULLFSYNC is currently
 53649  ** only available on Mac OS X.  But that could change.
 53650  */
 53651  #ifdef F_FULLFSYNC
 53652  # define HAVE_FULLFSYNC 1
 53653  #else
 53654  # define HAVE_FULLFSYNC 0
 53655  #endif
 53656  /*
 53657  ** The fsync() system call does not work as advertised on many
 53658  ** unix systems.  The following procedure is an attempt to make
 53659  ** it work better.
 53660  **
 53661  **
 53662  ** SQLite sets the dataOnly flag if the size of the file is unchanged.
 53663  ** The idea behind dataOnly is that it should only write the file content
 53664  ** to disk, not the inode.  We only set dataOnly if the file size is 
 53665  ** unchanged since the file size is part of the inode.  However, 
 53666  ** Ted Ts'o tells us that fdatasync() will also write the inode if the
 53667  ** file size has changed.  The only real difference between fdatasync()
 53668  ** and fsync(), Ted tells us, is that fdatasync() will not flush the
 53669  ** inode if the mtime or owner or other inode attributes have changed.
 53670  ** We only care about the file size, not the other file attributes, so
 53671  ** as far as SQLite is concerned, an fdatasync() is always adequate.
 53672  ** So, we always use fdatasync() if it is available, regardless of
 53673  ** the value of the dataOnly flag.
 53674  */
 53675  static int full_fsync(int fd, int fullSync, int dataOnly){
 53676    int rc;
 53677  #if HAVE_FULLFSYNC
 53678    SXUNUSED(dataOnly);
 53679  #else
 53680    SXUNUSED(fullSync);
 53681    SXUNUSED(dataOnly);
 53682  #endif
 53683  
 53684    /* If we compiled with the UNQLITE_NO_SYNC flag, then syncing is a
 53685    ** no-op
 53686    */
 53687  #if HAVE_FULLFSYNC
 53688    if( fullSync ){
 53689      rc = fcntl(fd, F_FULLFSYNC, 0);
 53690    }else{
 53691      rc = 1;
 53692    }
 53693    /* If the FULLFSYNC failed, fall back to attempting an fsync().
 53694    ** It shouldn't be possible for fullfsync to fail on the local 
 53695    ** file system (on OSX), so failure indicates that FULLFSYNC
 53696    ** isn't supported for this file system. So, attempt an fsync 
 53697    ** and (for now) ignore the overhead of a superfluous fcntl call.  
 53698    ** It'd be better to detect fullfsync support once and avoid 
 53699    ** the fcntl call every time sync is called.
 53700    */
 53701    if( rc ) rc = fsync(fd);
 53702  
 53703  #elif defined(__APPLE__)
 53704    /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
 53705    ** so currently we default to the macro that redefines fdatasync to fsync
 53706    */
 53707    rc = fsync(fd);
 53708  #else 
 53709    rc = fdatasync(fd);
 53710  #endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */
 53711    if( rc!= -1 ){
 53712      rc = 0;
 53713    }
 53714    return rc;
 53715  }
 53716  /*
 53717  ** Make sure all writes to a particular file are committed to disk.
 53718  **
 53719  ** If dataOnly==0 then both the file itself and its metadata (file
 53720  ** size, access time, etc) are synced.  If dataOnly!=0 then only the
 53721  ** file data is synced.
 53722  **
 53723  ** Under Unix, also make sure that the directory entry for the file
 53724  ** has been created by fsync-ing the directory that contains the file.
 53725  ** If we do not do this and we encounter a power failure, the directory
 53726  ** entry for the journal might not exist after we reboot.  The next
 53727  ** SQLite to access the file will not know that the journal exists (because
 53728  ** the directory entry for the journal was never created) and the transaction
 53729  ** will not roll back - possibly leading to database corruption.
 53730  */
 53731  static int unixSync(unqlite_file *id, int flags){
 53732    int rc;
 53733    unixFile *pFile = (unixFile*)id;
 53734  
 53735    int isDataOnly = (flags&UNQLITE_SYNC_DATAONLY);
 53736    int isFullsync = (flags&0x0F)==UNQLITE_SYNC_FULL;
 53737  
 53738    rc = full_fsync(pFile->h, isFullsync, isDataOnly);
 53739  
 53740    if( rc ){
 53741      pFile->lastErrno = errno;
 53742      return UNQLITE_IOERR;
 53743    }
 53744    if( pFile->dirfd>=0 ){
 53745      int err;
 53746  #ifndef UNQLITE_DISABLE_DIRSYNC
 53747      /* The directory sync is only attempted if full_fsync is
 53748      ** turned off or unavailable.  If a full_fsync occurred above,
 53749      ** then the directory sync is superfluous.
 53750      */
 53751      if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
 53752         /*
 53753         ** We have received multiple reports of fsync() returning
 53754         ** errors when applied to directories on certain file systems.
 53755         ** A failed directory sync is not a big deal.  So it seems
 53756         ** better to ignore the error.  Ticket #1657
 53757         */
 53758         /* pFile->lastErrno = errno; */
 53759         /* return UNQLITE_IOERR; */
 53760      }
 53761  #endif
 53762      err = close(pFile->dirfd); /* Only need to sync once, so close the */
 53763      if( err==0 ){              /* directory when we are done */
 53764        pFile->dirfd = -1;
 53765      }else{
 53766        pFile->lastErrno = errno;
 53767        rc = UNQLITE_IOERR;
 53768      }
 53769    }
 53770    return rc;
 53771  }
 53772  /*
 53773  ** Truncate an open file to a specified size
 53774  */
 53775  static int unixTruncate(unqlite_file *id, sxi64 nByte){
 53776    unixFile *pFile = (unixFile *)id;
 53777    int rc;
 53778  
 53779    rc = ftruncate(pFile->h, (off_t)nByte);
 53780    if( rc ){
 53781      pFile->lastErrno = errno;
 53782      return UNQLITE_IOERR;
 53783    }else{
 53784      return UNQLITE_OK;
 53785    }
 53786  }
 53787  /*
 53788  ** Determine the current size of a file in bytes
 53789  */
 53790  static int unixFileSize(unqlite_file *id,sxi64 *pSize){
 53791    int rc;
 53792    struct stat buf;
 53793    
 53794    rc = fstat(((unixFile*)id)->h, &buf);
 53795    
 53796    if( rc!=0 ){
 53797      ((unixFile*)id)->lastErrno = errno;
 53798      return UNQLITE_IOERR;
 53799    }
 53800    *pSize = buf.st_size;
 53801  
 53802    /* When opening a zero-size database, the findInodeInfo() procedure
 53803    ** writes a single byte into that file in order to work around a bug
 53804    ** in the OS-X msdos filesystem.  In order to avoid problems with upper
 53805    ** layers, we need to report this file size as zero even though it is
 53806    ** really 1.   Ticket #3260.
 53807    */
 53808    if( *pSize==1 ) *pSize = 0;
 53809  
 53810    return UNQLITE_OK;
 53811  }
 53812  /*
 53813  ** Return the sector size in bytes of the underlying block device for
 53814  ** the specified file. This is almost always 512 bytes, but may be
 53815  ** larger for some devices.
 53816  **
 53817  ** SQLite code assumes this function cannot fail. It also assumes that
 53818  ** if two files are created in the same file-system directory (i.e.
 53819  ** a database and its journal file) that the sector size will be the
 53820  ** same for both.
 53821  */
 53822  static int unixSectorSize(unqlite_file *NotUsed){
 53823    SXUNUSED(NotUsed);
 53824    return UNQLITE_DEFAULT_SECTOR_SIZE;
 53825  }
 53826  /*
 53827  ** This vector defines all the methods that can operate on an
 53828  ** unqlite_file for Windows systems.
 53829  */
 53830  static const unqlite_io_methods unixIoMethod = {
 53831    1,                              /* iVersion */
 53832    unixClose,                       /* xClose */
 53833    unixRead,                        /* xRead */
 53834    unixWrite,                       /* xWrite */
 53835    unixTruncate,                    /* xTruncate */
 53836    unixSync,                        /* xSync */
 53837    unixFileSize,                    /* xFileSize */
 53838    unixLock,                        /* xLock */
 53839    unixUnlock,                      /* xUnlock */
 53840    unixCheckReservedLock,           /* xCheckReservedLock */
 53841    unixSectorSize,                  /* xSectorSize */
 53842  };
 53843  /****************************************************************************
 53844  **************************** unqlite_vfs methods ****************************
 53845  **
 53846  ** This division contains the implementation of methods on the
 53847  ** unqlite_vfs object.
 53848  */
 53849  /*
 53850  ** Initialize the contents of the unixFile structure pointed to by pId.
 53851  */
 53852  static int fillInUnixFile(
 53853    unqlite_vfs *pVfs,      /* Pointer to vfs object */
 53854    int h,                  /* Open file descriptor of file being opened */
 53855    int dirfd,              /* Directory file descriptor */
 53856    unqlite_file *pId,      /* Write to the unixFile structure here */
 53857    const char *zFilename,  /* Name of the file being opened */
 53858    int noLock,             /* Omit locking if true */
 53859    int isDelete            /* Delete on close if true */
 53860  ){
 53861    const unqlite_io_methods *pLockingStyle = &unixIoMethod;
 53862    unixFile *pNew = (unixFile *)pId;
 53863    int rc = UNQLITE_OK;
 53864  
 53865    /* Parameter isDelete is only used on vxworks. Express this explicitly 
 53866    ** here to prevent compiler warnings about unused parameters.
 53867    */
 53868    SXUNUSED(isDelete);
 53869    SXUNUSED(noLock);
 53870    SXUNUSED(pVfs);
 53871  
 53872    pNew->h = h;
 53873    pNew->dirfd = dirfd;
 53874    pNew->fileFlags = 0;
 53875    pNew->zPath = zFilename;
 53876    
 53877    unixEnterMutex();
 53878    rc = findInodeInfo(pNew, &pNew->pInode);
 53879    if( rc!=UNQLITE_OK ){
 53880        /* If an error occured in findInodeInfo(), close the file descriptor
 53881        ** immediately, before releasing the mutex. findInodeInfo() may fail
 53882        ** in two scenarios:
 53883        **
 53884        **   (a) A call to fstat() failed.
 53885        **   (b) A malloc failed.
 53886        **
 53887        ** Scenario (b) may only occur if the process is holding no other
 53888        ** file descriptors open on the same file. If there were other file
 53889        ** descriptors on this file, then no malloc would be required by
 53890        ** findInodeInfo(). If this is the case, it is quite safe to close
 53891        ** handle h - as it is guaranteed that no posix locks will be released
 53892        ** by doing so.
 53893        **
 53894        ** If scenario (a) caused the error then things are not so safe. The
 53895        ** implicit assumption here is that if fstat() fails, things are in
 53896        ** such bad shape that dropping a lock or two doesn't matter much.
 53897        */
 53898        close(h);
 53899        h = -1;
 53900    }
 53901    unixLeaveMutex();
 53902    
 53903    pNew->lastErrno = 0;
 53904    if( rc!=UNQLITE_OK ){
 53905      if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */
 53906      if( h>=0 ) close(h);
 53907    }else{
 53908      pNew->pMethod = pLockingStyle;
 53909    }
 53910    return rc;
 53911  }
 53912  /*
 53913  ** Open a file descriptor to the directory containing file zFilename.
 53914  ** If successful, *pFd is set to the opened file descriptor and
 53915  ** UNQLITE_OK is returned. If an error occurs, either UNQLITE_NOMEM
 53916  ** or UNQLITE_CANTOPEN is returned and *pFd is set to an undefined
 53917  ** value.
 53918  **
 53919  ** If UNQLITE_OK is returned, the caller is responsible for closing
 53920  ** the file descriptor *pFd using close().
 53921  */
 53922  static int openDirectory(const char *zFilename, int *pFd){
 53923    sxu32 ii;
 53924    int fd = -1;
 53925    char zDirname[MAX_PATHNAME+1];
 53926    sxu32 n;
 53927    n = Systrcpy(zDirname,sizeof(zDirname),zFilename,0);
 53928    for(ii=n; ii>1 && zDirname[ii]!='/'; ii--);
 53929    if( ii>0 ){
 53930      zDirname[ii] = '\0';
 53931      fd = open(zDirname, O_RDONLY|O_BINARY, 0);
 53932      if( fd>=0 ){
 53933  #ifdef FD_CLOEXEC
 53934        fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
 53935  #endif
 53936      }
 53937    }
 53938    *pFd = fd;
 53939    return (fd>=0?UNQLITE_OK: UNQLITE_IOERR );
 53940  }
 53941  /*
 53942  ** Search for an unused file descriptor that was opened on the database 
 53943  ** file (not a journal or master-journal file) identified by pathname
 53944  ** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second
 53945  ** argument to this function.
 53946  **
 53947  ** Such a file descriptor may exist if a database connection was closed
 53948  ** but the associated file descriptor could not be closed because some
 53949  ** other file descriptor open on the same file is holding a file-lock.
 53950  ** Refer to comments in the unixClose() function and the lengthy comment
 53951  ** describing "Posix Advisory Locking" at the start of this file for 
 53952  ** further details. Also, ticket #4018.
 53953  **
 53954  ** If a suitable file descriptor is found, then it is returned. If no
 53955  ** such file descriptor is located, -1 is returned.
 53956  */
 53957  static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
 53958    UnixUnusedFd *pUnused = 0;
 53959    struct stat sStat;                   /* Results of stat() call */
 53960    /* A stat() call may fail for various reasons. If this happens, it is
 53961    ** almost certain that an open() call on the same path will also fail.
 53962    ** For this reason, if an error occurs in the stat() call here, it is
 53963    ** ignored and -1 is returned. The caller will try to open a new file
 53964    ** descriptor on the same path, fail, and return an error to SQLite.
 53965    **
 53966    ** Even if a subsequent open() call does succeed, the consequences of
 53967    ** not searching for a resusable file descriptor are not dire.  */
 53968    if( 0==stat(zPath, &sStat) ){
 53969      unixInodeInfo *pInode;
 53970  
 53971      unixEnterMutex();
 53972      pInode = inodeList;
 53973      while( pInode && (pInode->fileId.dev!=sStat.st_dev
 53974                       || pInode->fileId.ino!=sStat.st_ino) ){
 53975         pInode = pInode->pNext;
 53976      }
 53977      if( pInode ){
 53978        UnixUnusedFd **pp;
 53979        for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
 53980        pUnused = *pp;
 53981        if( pUnused ){
 53982          *pp = pUnused->pNext;
 53983        }
 53984      }
 53985      unixLeaveMutex();
 53986    }
 53987    return pUnused;
 53988  }
 53989  /*
 53990  ** This function is called by unixOpen() to determine the unix permissions
 53991  ** to create new files with. If no error occurs, then UNQLITE_OK is returned
 53992  ** and a value suitable for passing as the third argument to open(2) is
 53993  ** written to *pMode. If an IO error occurs, an SQLite error code is 
 53994  ** returned and the value of *pMode is not modified.
 53995  **
 53996  ** If the file being opened is a temporary file, it is always created with
 53997  ** the octal permissions 0600 (read/writable by owner only). If the file
 53998  ** is a database or master journal file, it is created with the permissions 
 53999  ** mask UNQLITE_DEFAULT_FILE_PERMISSIONS.
 54000  **
 54001  ** Finally, if the file being opened is a WAL or regular journal file, then 
 54002  ** this function queries the file-system for the permissions on the 
 54003  ** corresponding database file and sets *pMode to this value. Whenever 
 54004  ** possible, WAL and journal files are created using the same permissions 
 54005  ** as the associated database file.
 54006  */
 54007  static int findCreateFileMode(
 54008    const char *zPath,              /* Path of file (possibly) being created */
 54009    int flags,                      /* Flags passed as 4th argument to xOpen() */
 54010    mode_t *pMode                   /* OUT: Permissions to open file with */
 54011  ){
 54012    int rc = UNQLITE_OK;             /* Return Code */
 54013    if( flags & UNQLITE_OPEN_TEMP_DB ){
 54014      *pMode = 0600;
 54015       SXUNUSED(zPath);
 54016    }else{
 54017      *pMode = UNQLITE_DEFAULT_FILE_PERMISSIONS;
 54018    }
 54019    return rc;
 54020  }
 54021  /*
 54022  ** Open the file zPath.
 54023  ** 
 54024  ** Previously, the SQLite OS layer used three functions in place of this
 54025  ** one:
 54026  **
 54027  **     unqliteOsOpenReadWrite();
 54028  **     unqliteOsOpenReadOnly();
 54029  **     unqliteOsOpenExclusive();
 54030  **
 54031  ** These calls correspond to the following combinations of flags:
 54032  **
 54033  **     ReadWrite() ->     (READWRITE | CREATE)
 54034  **     ReadOnly()  ->     (READONLY) 
 54035  **     OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
 54036  **
 54037  ** The old OpenExclusive() accepted a boolean argument - "delFlag". If
 54038  ** true, the file was configured to be automatically deleted when the
 54039  ** file handle closed. To achieve the same effect using this new 
 54040  ** interface, add the DELETEONCLOSE flag to those specified above for 
 54041  ** OpenExclusive().
 54042  */
 54043  static int unixOpen(
 54044    unqlite_vfs *pVfs,           /* The VFS for which this is the xOpen method */
 54045    const char *zPath,           /* Pathname of file to be opened */
 54046    unqlite_file *pFile,         /* The file descriptor to be filled in */
 54047    unsigned int flags           /* Input flags to control the opening */
 54048  ){
 54049    unixFile *p = (unixFile *)pFile;
 54050    int fd = -1;                   /* File descriptor returned by open() */
 54051    int dirfd = -1;                /* Directory file descriptor */
 54052    int openFlags = 0;             /* Flags to pass to open() */
 54053    int noLock;                    /* True to omit locking primitives */
 54054    int rc = UNQLITE_OK;            /* Function Return Code */
 54055    UnixUnusedFd *pUnused;
 54056    int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
 54057    int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
 54058    int isCreate     = (flags & UNQLITE_OPEN_CREATE);
 54059    int isReadonly   = (flags & UNQLITE_OPEN_READONLY);
 54060    int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
 54061    /* If creating a master or main-file journal, this function will open
 54062    ** a file-descriptor on the directory too. The first time unixSync()
 54063    ** is called the directory file descriptor will be fsync()ed and close()d.
 54064    */
 54065    int isOpenDirectory = isCreate ;
 54066    const char *zName = zPath;
 54067  
 54068    SyZero(p,sizeof(unixFile));
 54069    
 54070    pUnused = findReusableFd(zName, flags);
 54071    if( pUnused ){
 54072  	  fd = pUnused->fd;
 54073    }else{
 54074  	  pUnused = unqlite_malloc(sizeof(*pUnused));
 54075        if( !pUnused ){
 54076          return UNQLITE_NOMEM;
 54077        }
 54078    }
 54079    p->pUnused = pUnused;
 54080    
 54081    /* Determine the value of the flags parameter passed to POSIX function
 54082    ** open(). These must be calculated even if open() is not called, as
 54083    ** they may be stored as part of the file handle and used by the 
 54084    ** 'conch file' locking functions later on.  */
 54085    if( isReadonly )  openFlags |= O_RDONLY;
 54086    if( isReadWrite ) openFlags |= O_RDWR;
 54087    if( isCreate )    openFlags |= O_CREAT;
 54088    if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
 54089    openFlags |= (O_LARGEFILE|O_BINARY);
 54090  
 54091    if( fd<0 ){
 54092      mode_t openMode;              /* Permissions to create file with */
 54093      rc = findCreateFileMode(zName, flags, &openMode);
 54094      if( rc!=UNQLITE_OK ){
 54095        return rc;
 54096      }
 54097      fd = open(zName, openFlags, openMode);
 54098      if( fd<0 ){
 54099  	  rc = UNQLITE_IOERR;
 54100        goto open_finished;
 54101      }
 54102    }
 54103    
 54104    if( p->pUnused ){
 54105      p->pUnused->fd = fd;
 54106      p->pUnused->flags = flags;
 54107    }
 54108  
 54109    if( isDelete ){
 54110      unlink(zName);
 54111    }
 54112  
 54113    if( isOpenDirectory ){
 54114      rc = openDirectory(zPath, &dirfd);
 54115      if( rc!=UNQLITE_OK ){
 54116        /* It is safe to close fd at this point, because it is guaranteed not
 54117        ** to be open on a database file. If it were open on a database file,
 54118        ** it would not be safe to close as this would release any locks held
 54119        ** on the file by this process.  */
 54120        close(fd);             /* silently leak if fail, already in error */
 54121        goto open_finished;
 54122      }
 54123    }
 54124  
 54125  #ifdef FD_CLOEXEC
 54126    fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
 54127  #endif
 54128  
 54129    noLock = 0;
 54130  
 54131  #if defined(__APPLE__) 
 54132    struct statfs fsInfo;
 54133    if( fstatfs(fd, &fsInfo) == -1 ){
 54134      ((unixFile*)pFile)->lastErrno = errno;
 54135      if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
 54136      close(fd); /* silently leak if fail, in error */
 54137      return UNQLITE_IOERR;
 54138    }
 54139    if (0 == SyStrncmp("msdos", fsInfo.f_fstypename, 5)) {
 54140      ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS;
 54141    }
 54142  #endif
 54143    
 54144    rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
 54145  open_finished:
 54146    if( rc!=UNQLITE_OK ){
 54147      unqlite_free(p->pUnused);
 54148    }
 54149    return rc;
 54150  }
 54151  /*
 54152  ** Delete the file at zPath. If the dirSync argument is true, fsync()
 54153  ** the directory after deleting the file.
 54154  */
 54155  static int unixDelete(
 54156    unqlite_vfs *NotUsed,     /* VFS containing this as the xDelete method */
 54157    const char *zPath,        /* Name of file to be deleted */
 54158    int dirSync               /* If true, fsync() directory after deleting file */
 54159  ){
 54160    int rc = UNQLITE_OK;
 54161    SXUNUSED(NotUsed);
 54162    
 54163    if( unlink(zPath)==(-1) && errno!=ENOENT ){
 54164  	  return UNQLITE_IOERR;
 54165    }
 54166  #ifndef UNQLITE_DISABLE_DIRSYNC
 54167    if( dirSync ){
 54168      int fd;
 54169      rc = openDirectory(zPath, &fd);
 54170      if( rc==UNQLITE_OK ){
 54171        if( fsync(fd) )
 54172        {
 54173          rc = UNQLITE_IOERR;
 54174        }
 54175        if( close(fd) && !rc ){
 54176          rc = UNQLITE_IOERR;
 54177        }
 54178      }
 54179    }
 54180  #endif
 54181    return rc;
 54182  }
 54183  /*
 54184  ** Sleep for a little while.  Return the amount of time slept.
 54185  ** The argument is the number of microseconds we want to sleep.
 54186  ** The return value is the number of microseconds of sleep actually
 54187  ** requested from the underlying operating system, a number which
 54188  ** might be greater than or equal to the argument, but not less
 54189  ** than the argument.
 54190  */
 54191  static int unixSleep(unqlite_vfs *NotUsed, int microseconds)
 54192  {
 54193  #if defined(HAVE_USLEEP) && HAVE_USLEEP
 54194    usleep(microseconds);
 54195    SXUNUSED(NotUsed);
 54196    return microseconds;
 54197  #else
 54198    int seconds = (microseconds+999999)/1000000;
 54199    SXUNUSED(NotUsed);
 54200    sleep(seconds);
 54201    return seconds*1000000;
 54202  #endif
 54203  }
 54204  /*
 54205   * Export the current system time.
 54206   */
 54207  static int unixCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
 54208  {
 54209  	struct tm *pTm;
 54210  	time_t tt;
 54211  	SXUNUSED(pVfs);
 54212  	time(&tt);
 54213  	pTm = gmtime(&tt);
 54214  	if( pTm ){ /* Yes, it can fail */
 54215  		STRUCT_TM_TO_SYTM(pTm,pOut);
 54216  	}
 54217  	return UNQLITE_OK;
 54218  }
 54219  /*
 54220  ** Test the existance of or access permissions of file zPath. The
 54221  ** test performed depends on the value of flags:
 54222  **
 54223  **     UNQLITE_ACCESS_EXISTS: Return 1 if the file exists
 54224  **     UNQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
 54225  **     UNQLITE_ACCESS_READONLY: Return 1 if the file is readable.
 54226  **
 54227  ** Otherwise return 0.
 54228  */
 54229  static int unixAccess(
 54230    unqlite_vfs *NotUsed,   /* The VFS containing this xAccess method */
 54231    const char *zPath,      /* Path of the file to examine */
 54232    int flags,              /* What do we want to learn about the zPath file? */
 54233    int *pResOut            /* Write result boolean here */
 54234  ){
 54235    int amode = 0;
 54236    SXUNUSED(NotUsed);
 54237    switch( flags ){
 54238      case UNQLITE_ACCESS_EXISTS:
 54239        amode = F_OK;
 54240        break;
 54241      case UNQLITE_ACCESS_READWRITE:
 54242        amode = W_OK|R_OK;
 54243        break;
 54244      case UNQLITE_ACCESS_READ:
 54245        amode = R_OK;
 54246        break;
 54247      default:
 54248  		/* Can't happen */
 54249        break;
 54250    }
 54251    *pResOut = (access(zPath, amode)==0);
 54252    if( flags==UNQLITE_ACCESS_EXISTS && *pResOut ){
 54253      struct stat buf;
 54254      if( 0==stat(zPath, &buf) && buf.st_size==0 ){
 54255        *pResOut = 0;
 54256      }
 54257    }
 54258    return UNQLITE_OK;
 54259  }
 54260  /*
 54261  ** Turn a relative pathname into a full pathname. The relative path
 54262  ** is stored as a nul-terminated string in the buffer pointed to by
 54263  ** zPath. 
 54264  **
 54265  ** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes 
 54266  ** (in this case, MAX_PATHNAME bytes). The full-path is written to
 54267  ** this buffer before returning.
 54268  */
 54269  static int unixFullPathname(
 54270    unqlite_vfs *pVfs,            /* Pointer to vfs object */
 54271    const char *zPath,            /* Possibly relative input path */
 54272    int nOut,                     /* Size of output buffer in bytes */
 54273    char *zOut                    /* Output buffer */
 54274  ){
 54275    if( zPath[0]=='/' ){
 54276  	  Systrcpy(zOut,(sxu32)nOut,zPath,0);
 54277  	  SXUNUSED(pVfs);
 54278    }else{
 54279      sxu32 nCwd;
 54280  	zOut[nOut-1] = '\0';
 54281      if( getcwd(zOut, nOut-1)==0 ){
 54282  		return UNQLITE_IOERR;
 54283      }
 54284      nCwd = SyStrlen(zOut);
 54285      SyBufferFormat(&zOut[nCwd],(sxu32)nOut-nCwd,"/%s",zPath);
 54286    }
 54287    return UNQLITE_OK;
 54288  }
 54289  /*
 54290   * Export the Unix Vfs.
 54291   */
 54292  UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
 54293  {
 54294  	static const unqlite_vfs sUnixvfs = {
 54295  		"Unix",              /* Vfs name */
 54296  		1,                   /* Vfs structure version */
 54297  		sizeof(unixFile),    /* szOsFile */
 54298  		MAX_PATHNAME,        /* mxPathName */
 54299  		unixOpen,            /* xOpen */
 54300  		unixDelete,          /* xDelete */
 54301  		unixAccess,          /* xAccess */
 54302  		unixFullPathname,    /* xFullPathname */
 54303  		0,                   /* xTmp */
 54304  		unixSleep,           /* xSleep */
 54305  		unixCurrentTime,     /* xCurrentTime */
 54306  		0,                   /* xGetLastError */
 54307  	};
 54308  	return &sUnixvfs;
 54309  }
 54310  
 54311  #endif /* __UNIXES__ */
 54312  
 54313  /*
 54314   * ----------------------------------------------------------
 54315   * File: os_win.c
 54316   * MD5: ab70fb386c21b39a08b0eb776a8391ab
 54317   * ----------------------------------------------------------
 54318   */
 54319  /*
 54320   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 54321   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 54322   * Version 1.1.6
 54323   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 54324   * please contact Symisc Systems via:
 54325   *       legal@symisc.net
 54326   *       licensing@symisc.net
 54327   *       contact@symisc.net
 54328   * or visit:
 54329   *      http://unqlite.org/licensing.html
 54330   */
 54331   /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel <chm@symisc.net> $ */
 54332  #ifndef UNQLITE_AMALGAMATION
 54333  #include "unqliteInt.h"
 54334  #endif
 54335  /* Omit the whole layer from the build if compiling for platforms other than Windows */
 54336  #ifdef __WINNT__
 54337  /* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */
 54338  #include <Windows.h>
 54339  /*
 54340  ** Some microsoft compilers lack this definition.
 54341  */
 54342  #ifndef INVALID_FILE_ATTRIBUTES
 54343  # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 
 54344  #endif
 54345  /*
 54346  ** WinCE lacks native support for file locking so we have to fake it
 54347  ** with some code of our own.
 54348  */
 54349  #ifdef __WIN_CE__
 54350  typedef struct winceLock {
 54351    int nReaders;       /* Number of reader locks obtained */
 54352    BOOL bPending;      /* Indicates a pending lock has been obtained */
 54353    BOOL bReserved;     /* Indicates a reserved lock has been obtained */
 54354    BOOL bExclusive;    /* Indicates an exclusive lock has been obtained */
 54355  } winceLock;
 54356  #define AreFileApisANSI() 1
 54357  #define FormatMessageW(a,b,c,d,e,f,g) 0
 54358  #endif
 54359  
 54360  /*
 54361  ** The winFile structure is a subclass of unqlite_file* specific to the win32
 54362  ** portability layer.
 54363  */
 54364  typedef struct winFile winFile;
 54365  struct winFile {
 54366    const unqlite_io_methods *pMethod; /*** Must be first ***/
 54367    unqlite_vfs *pVfs;      /* The VFS used to open this file */
 54368    HANDLE h;               /* Handle for accessing the file */
 54369    sxu8 locktype;          /* Type of lock currently held on this file */
 54370    short sharedLockByte;   /* Randomly chosen byte used as a shared lock */
 54371    DWORD lastErrno;        /* The Windows errno from the last I/O error */
 54372    DWORD sectorSize;       /* Sector size of the device file is on */
 54373    int szChunk;            /* Chunk size */
 54374  #ifdef __WIN_CE__
 54375    WCHAR *zDeleteOnClose;  /* Name of file to delete when closing */
 54376    HANDLE hMutex;          /* Mutex used to control access to shared lock */  
 54377    HANDLE hShared;         /* Shared memory segment used for locking */
 54378    winceLock local;        /* Locks obtained by this instance of winFile */
 54379    winceLock *shared;      /* Global shared lock memory for the file  */
 54380  #endif
 54381  };
 54382  /*
 54383  ** Convert a UTF-8 string to microsoft unicode (UTF-16?). 
 54384  **
 54385  ** Space to hold the returned string is obtained from HeapAlloc().
 54386  */
 54387  static WCHAR *utf8ToUnicode(const char *zFilename){
 54388    int nChar;
 54389    WCHAR *zWideFilename;
 54390  
 54391    nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
 54392    zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) );
 54393    if( zWideFilename==0 ){
 54394      return 0;
 54395    }
 54396    nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
 54397    if( nChar==0 ){
 54398      HeapFree(GetProcessHeap(),0,zWideFilename);
 54399      zWideFilename = 0;
 54400    }
 54401    return zWideFilename;
 54402  }
 54403  
 54404  /*
 54405  ** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
 54406  ** obtained from malloc().
 54407  */
 54408  static char *unicodeToUtf8(const WCHAR *zWideFilename){
 54409    int nByte;
 54410    char *zFilename;
 54411  
 54412    nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
 54413    zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte );
 54414    if( zFilename==0 ){
 54415      return 0;
 54416    }
 54417    nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
 54418                                0, 0);
 54419    if( nByte == 0 ){
 54420      HeapFree(GetProcessHeap(),0,zFilename);
 54421      zFilename = 0;
 54422    }
 54423    return zFilename;
 54424  }
 54425  
 54426  /*
 54427  ** Convert an ansi string to microsoft unicode, based on the
 54428  ** current codepage settings for file apis.
 54429  ** 
 54430  ** Space to hold the returned string is obtained
 54431  ** from malloc.
 54432  */
 54433  static WCHAR *mbcsToUnicode(const char *zFilename){
 54434    int nByte;
 54435    WCHAR *zMbcsFilename;
 54436    int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
 54437  
 54438    nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR);
 54439    zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) );
 54440    if( zMbcsFilename==0 ){
 54441      return 0;
 54442    }
 54443    nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
 54444    if( nByte==0 ){
 54445      HeapFree(GetProcessHeap(),0,zMbcsFilename);
 54446      zMbcsFilename = 0;
 54447    }
 54448    return zMbcsFilename;
 54449  }
 54450  /*
 54451  ** Convert multibyte character string to UTF-8.  Space to hold the
 54452  ** returned string is obtained from malloc().
 54453  */
 54454  char *unqlite_win32_mbcs_to_utf8(const char *zFilename){
 54455    char *zFilenameUtf8;
 54456    WCHAR *zTmpWide;
 54457  
 54458    zTmpWide = mbcsToUnicode(zFilename);
 54459    if( zTmpWide==0 ){
 54460      return 0;
 54461    }
 54462    zFilenameUtf8 = unicodeToUtf8(zTmpWide);
 54463    HeapFree(GetProcessHeap(),0,zTmpWide);
 54464    return zFilenameUtf8;
 54465  }
 54466  /*
 54467  ** Some microsoft compilers lack this definition.
 54468  */
 54469  #ifndef INVALID_SET_FILE_POINTER
 54470  # define INVALID_SET_FILE_POINTER ((DWORD)-1)
 54471  #endif
 54472  
 54473  /*
 54474  ** Move the current position of the file handle passed as the first 
 54475  ** argument to offset iOffset within the file. If successful, return 0. 
 54476  ** Otherwise, set pFile->lastErrno and return non-zero.
 54477  */
 54478  static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){
 54479    LONG upperBits;                 /* Most sig. 32 bits of new offset */
 54480    LONG lowerBits;                 /* Least sig. 32 bits of new offset */
 54481    DWORD dwRet;                    /* Value returned by SetFilePointer() */
 54482  
 54483    upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
 54484    lowerBits = (LONG)(iOffset & 0xffffffff);
 54485  
 54486    /* API oddity: If successful, SetFilePointer() returns a dword 
 54487    ** containing the lower 32-bits of the new file-offset. Or, if it fails,
 54488    ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, 
 54489    ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine 
 54490    ** whether an error has actually occured, it is also necessary to call 
 54491    ** GetLastError().
 54492    */
 54493    dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
 54494    if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
 54495      pFile->lastErrno = GetLastError();
 54496      return 1;
 54497    }
 54498    return 0;
 54499  }
 54500  /*
 54501  ** Close a file.
 54502  **
 54503  ** It is reported that an attempt to close a handle might sometimes
 54504  ** fail.  This is a very unreasonable result, but windows is notorious
 54505  ** for being unreasonable so I do not doubt that it might happen.  If
 54506  ** the close fails, we pause for 100 milliseconds and try again.  As
 54507  ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
 54508  ** giving up and returning an error.
 54509  */
 54510  #define MX_CLOSE_ATTEMPT 3
 54511  static int winClose(unqlite_file *id)
 54512  {
 54513    int rc, cnt = 0;
 54514    winFile *pFile = (winFile*)id;
 54515    do{
 54516      rc = CloseHandle(pFile->h);
 54517    }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
 54518  
 54519    return rc ? UNQLITE_OK : UNQLITE_IOERR;
 54520  }
 54521  /*
 54522  ** Read data from a file into a buffer.  Return UNQLITE_OK if all
 54523  ** bytes were read successfully and UNQLITE_IOERR if anything goes
 54524  ** wrong.
 54525  */
 54526  static int winRead(
 54527    unqlite_file *id,          /* File to read from */
 54528    void *pBuf,                /* Write content into this buffer */
 54529    unqlite_int64 amt,        /* Number of bytes to read */
 54530    unqlite_int64 offset       /* Begin reading at this offset */
 54531  ){
 54532    winFile *pFile = (winFile*)id;  /* file handle */
 54533    DWORD nRead;                    /* Number of bytes actually read from file */
 54534  
 54535    if( seekWinFile(pFile, offset) ){
 54536      return UNQLITE_FULL;
 54537    }
 54538    if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){
 54539      pFile->lastErrno = GetLastError();
 54540      return UNQLITE_IOERR;
 54541    }
 54542    if( nRead<(DWORD)amt ){
 54543      /* Unread parts of the buffer must be zero-filled */
 54544      SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead));
 54545      return UNQLITE_IOERR;
 54546    }
 54547  
 54548    return UNQLITE_OK;
 54549  }
 54550  
 54551  /*
 54552  ** Write data from a buffer into a file.  Return UNQLITE_OK on success
 54553  ** or some other error code on failure.
 54554  */
 54555  static int winWrite(
 54556    unqlite_file *id,               /* File to write into */
 54557    const void *pBuf,               /* The bytes to be written */
 54558    unqlite_int64 amt,                        /* Number of bytes to write */
 54559    unqlite_int64 offset            /* Offset into the file to begin writing at */
 54560  ){
 54561    int rc;                         /* True if error has occured, else false */
 54562    winFile *pFile = (winFile*)id;  /* File handle */
 54563  
 54564    rc = seekWinFile(pFile, offset);
 54565    if( rc==0 ){
 54566      sxu8 *aRem = (sxu8 *)pBuf;        /* Data yet to be written */
 54567      unqlite_int64 nRem = amt;         /* Number of bytes yet to be written */
 54568      DWORD nWrite;                 /* Bytes written by each WriteFile() call */
 54569  
 54570      while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){
 54571        aRem += nWrite;
 54572        nRem -= nWrite;
 54573      }
 54574      if( nRem>0 ){
 54575        pFile->lastErrno = GetLastError();
 54576        rc = 1;
 54577      }
 54578    }
 54579    if( rc ){
 54580      if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
 54581        return UNQLITE_FULL;
 54582      }
 54583      return UNQLITE_IOERR;
 54584    }
 54585    return UNQLITE_OK;
 54586  }
 54587  
 54588  /*
 54589  ** Truncate an open file to a specified size
 54590  */
 54591  static int winTruncate(unqlite_file *id, unqlite_int64 nByte){
 54592    winFile *pFile = (winFile*)id;  /* File handle object */
 54593    int rc = UNQLITE_OK;             /* Return code for this function */
 54594  
 54595  
 54596    /* If the user has configured a chunk-size for this file, truncate the
 54597    ** file so that it consists of an integer number of chunks (i.e. the
 54598    ** actual file size after the operation may be larger than the requested
 54599    ** size).
 54600    */
 54601    if( pFile->szChunk ){
 54602      nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
 54603    }
 54604  
 54605    /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
 54606    if( seekWinFile(pFile, nByte) ){
 54607      rc = UNQLITE_IOERR;
 54608    }else if( 0==SetEndOfFile(pFile->h) ){
 54609      pFile->lastErrno = GetLastError();
 54610      rc = UNQLITE_IOERR;
 54611    }
 54612    return rc;
 54613  }
 54614  /*
 54615  ** Make sure all writes to a particular file are committed to disk.
 54616  */
 54617  static int winSync(unqlite_file *id, int flags){
 54618    winFile *pFile = (winFile*)id;
 54619    SXUNUSED(flags); /* MSVC warning */
 54620    if( FlushFileBuffers(pFile->h) ){
 54621      return UNQLITE_OK;
 54622    }else{
 54623      pFile->lastErrno = GetLastError();
 54624      return UNQLITE_IOERR;
 54625    }
 54626  }
 54627  /*
 54628  ** Determine the current size of a file in bytes
 54629  */
 54630  static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){
 54631    DWORD upperBits;
 54632    DWORD lowerBits;
 54633    winFile *pFile = (winFile*)id;
 54634    DWORD error;
 54635    lowerBits = GetFileSize(pFile->h, &upperBits);
 54636    if(   (lowerBits == INVALID_FILE_SIZE)
 54637       && ((error = GetLastError()) != NO_ERROR) )
 54638    {
 54639      pFile->lastErrno = error;
 54640      return UNQLITE_IOERR;
 54641    }
 54642    *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits;
 54643    return UNQLITE_OK;
 54644  }
 54645  /*
 54646  ** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
 54647  */
 54648  #ifndef LOCKFILE_FAIL_IMMEDIATELY
 54649  # define LOCKFILE_FAIL_IMMEDIATELY 1
 54650  #endif
 54651  
 54652  /*
 54653  ** Acquire a reader lock.
 54654  */
 54655  static int getReadLock(winFile *pFile){
 54656    int res;
 54657    OVERLAPPED ovlp;
 54658    ovlp.Offset = SHARED_FIRST;
 54659    ovlp.OffsetHigh = 0;
 54660    ovlp.hEvent = 0;
 54661    res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp);
 54662    if( res == 0 ){
 54663      pFile->lastErrno = GetLastError();
 54664    }
 54665    return res;
 54666  }
 54667  /*
 54668  ** Undo a readlock
 54669  */
 54670  static int unlockReadLock(winFile *pFile){
 54671    int res;
 54672    res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
 54673    if( res == 0 ){
 54674      pFile->lastErrno = GetLastError();
 54675    }
 54676    return res;
 54677  }
 54678  /*
 54679  ** Lock the file with the lock specified by parameter locktype - one
 54680  ** of the following:
 54681  **
 54682  **     (1) SHARED_LOCK
 54683  **     (2) RESERVED_LOCK
 54684  **     (3) PENDING_LOCK
 54685  **     (4) EXCLUSIVE_LOCK
 54686  **
 54687  ** Sometimes when requesting one lock state, additional lock states
 54688  ** are inserted in between.  The locking might fail on one of the later
 54689  ** transitions leaving the lock state different from what it started but
 54690  ** still short of its goal.  The following chart shows the allowed
 54691  ** transitions and the inserted intermediate states:
 54692  **
 54693  **    UNLOCKED -> SHARED
 54694  **    SHARED -> RESERVED
 54695  **    SHARED -> (PENDING) -> EXCLUSIVE
 54696  **    RESERVED -> (PENDING) -> EXCLUSIVE
 54697  **    PENDING -> EXCLUSIVE
 54698  **
 54699  ** This routine will only increase a lock.  The winUnlock() routine
 54700  ** erases all locks at once and returns us immediately to locking level 0.
 54701  ** It is not possible to lower the locking level one step at a time.  You
 54702  ** must go straight to locking level 0.
 54703  */
 54704  static int winLock(unqlite_file *id, int locktype){
 54705    int rc = UNQLITE_OK;    /* Return code from subroutines */
 54706    int res = 1;           /* Result of a windows lock call */
 54707    int newLocktype;       /* Set pFile->locktype to this value before exiting */
 54708    int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
 54709    winFile *pFile = (winFile*)id;
 54710    DWORD error = NO_ERROR;
 54711  
 54712    /* If there is already a lock of this type or more restrictive on the
 54713    ** OsFile, do nothing.
 54714    */
 54715    if( pFile->locktype>=locktype ){
 54716      return UNQLITE_OK;
 54717    }
 54718  
 54719    /* Make sure the locking sequence is correct
 54720    assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
 54721    assert( locktype!=PENDING_LOCK );
 54722    assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
 54723    */
 54724    /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
 54725    ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
 54726    ** the PENDING_LOCK byte is temporary.
 54727    */
 54728    newLocktype = pFile->locktype;
 54729    if(   (pFile->locktype==NO_LOCK)
 54730       || (   (locktype==EXCLUSIVE_LOCK)
 54731           && (pFile->locktype==RESERVED_LOCK))
 54732    ){
 54733      int cnt = 3;
 54734      while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
 54735        /* Try 3 times to get the pending lock.  The pending lock might be
 54736        ** held by another reader process who will release it momentarily.
 54737  	  */
 54738        Sleep(1);
 54739      }
 54740      gotPendingLock = res;
 54741      if( !res ){
 54742        error = GetLastError();
 54743      }
 54744    }
 54745  
 54746    /* Acquire a shared lock
 54747    */
 54748    if( locktype==SHARED_LOCK && res ){
 54749     /* assert( pFile->locktype==NO_LOCK ); */
 54750      res = getReadLock(pFile);
 54751      if( res ){
 54752        newLocktype = SHARED_LOCK;
 54753      }else{
 54754        error = GetLastError();
 54755      }
 54756    }
 54757  
 54758    /* Acquire a RESERVED lock
 54759    */
 54760    if( locktype==RESERVED_LOCK && res ){
 54761      /* assert( pFile->locktype==SHARED_LOCK ); */
 54762      res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
 54763      if( res ){
 54764        newLocktype = RESERVED_LOCK;
 54765      }else{
 54766        error = GetLastError();
 54767      }
 54768    }
 54769  
 54770    /* Acquire a PENDING lock
 54771    */
 54772    if( locktype==EXCLUSIVE_LOCK && res ){
 54773      newLocktype = PENDING_LOCK;
 54774      gotPendingLock = 0;
 54775    }
 54776  
 54777    /* Acquire an EXCLUSIVE lock
 54778    */
 54779    if( locktype==EXCLUSIVE_LOCK && res ){
 54780      /* assert( pFile->locktype>=SHARED_LOCK ); */
 54781      res = unlockReadLock(pFile);
 54782      res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
 54783      if( res ){
 54784        newLocktype = EXCLUSIVE_LOCK;
 54785      }else{
 54786        error = GetLastError();
 54787        getReadLock(pFile);
 54788      }
 54789    }
 54790  
 54791    /* If we are holding a PENDING lock that ought to be released, then
 54792    ** release it now.
 54793    */
 54794    if( gotPendingLock && locktype==SHARED_LOCK ){
 54795      UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
 54796    }
 54797  
 54798    /* Update the state of the lock has held in the file descriptor then
 54799    ** return the appropriate result code.
 54800    */
 54801    if( res ){
 54802      rc = UNQLITE_OK;
 54803    }else{
 54804      pFile->lastErrno = error;
 54805      rc = UNQLITE_BUSY;
 54806    }
 54807    pFile->locktype = (sxu8)newLocktype;
 54808    return rc;
 54809  }
 54810  /*
 54811  ** This routine checks if there is a RESERVED lock held on the specified
 54812  ** file by this or any other process. If such a lock is held, return
 54813  ** non-zero, otherwise zero.
 54814  */
 54815  static int winCheckReservedLock(unqlite_file *id, int *pResOut){
 54816    int rc;
 54817    winFile *pFile = (winFile*)id;
 54818    if( pFile->locktype>=RESERVED_LOCK ){
 54819      rc = 1;
 54820    }else{
 54821      rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
 54822      if( rc ){
 54823        UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
 54824      }
 54825      rc = !rc;
 54826    }
 54827    *pResOut = rc;
 54828    return UNQLITE_OK;
 54829  }
 54830  /*
 54831  ** Lower the locking level on file descriptor id to locktype.  locktype
 54832  ** must be either NO_LOCK or SHARED_LOCK.
 54833  **
 54834  ** If the locking level of the file descriptor is already at or below
 54835  ** the requested locking level, this routine is a no-op.
 54836  **
 54837  ** It is not possible for this routine to fail if the second argument
 54838  ** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine
 54839  ** might return UNQLITE_IOERR;
 54840  */
 54841  static int winUnlock(unqlite_file *id, int locktype){
 54842    int type;
 54843    winFile *pFile = (winFile*)id;
 54844    int rc = UNQLITE_OK;
 54845  
 54846    type = pFile->locktype;
 54847    if( type>=EXCLUSIVE_LOCK ){
 54848      UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
 54849      if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
 54850        /* This should never happen.  We should always be able to
 54851        ** reacquire the read lock */
 54852        rc = UNQLITE_IOERR;
 54853      }
 54854    }
 54855    if( type>=RESERVED_LOCK ){
 54856      UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
 54857    }
 54858    if( locktype==NO_LOCK && type>=SHARED_LOCK ){
 54859      unlockReadLock(pFile);
 54860    }
 54861    if( type>=PENDING_LOCK ){
 54862      UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
 54863    }
 54864    pFile->locktype = (sxu8)locktype;
 54865    return rc;
 54866  }
 54867  /*
 54868  ** Return the sector size in bytes of the underlying block device for
 54869  ** the specified file. This is almost always 512 bytes, but may be
 54870  ** larger for some devices.
 54871  **
 54872  */
 54873  static int winSectorSize(unqlite_file *id){
 54874    return (int)(((winFile*)id)->sectorSize);
 54875  }
 54876  /*
 54877  ** This vector defines all the methods that can operate on an
 54878  ** unqlite_file for Windows systems.
 54879  */
 54880  static const unqlite_io_methods winIoMethod = {
 54881    1,                              /* iVersion */
 54882    winClose,                       /* xClose */
 54883    winRead,                        /* xRead */
 54884    winWrite,                       /* xWrite */
 54885    winTruncate,                    /* xTruncate */
 54886    winSync,                        /* xSync */
 54887    winFileSize,                    /* xFileSize */
 54888    winLock,                        /* xLock */
 54889    winUnlock,                      /* xUnlock */
 54890    winCheckReservedLock,           /* xCheckReservedLock */
 54891    winSectorSize,                  /* xSectorSize */
 54892  };
 54893  /*
 54894   * Windows VFS Methods.
 54895   */
 54896  /*
 54897  ** Convert a UTF-8 filename into whatever form the underlying
 54898  ** operating system wants filenames in.  Space to hold the result
 54899  ** is obtained from malloc and must be freed by the calling
 54900  ** function.
 54901  */
 54902  static void *convertUtf8Filename(const char *zFilename)
 54903  {
 54904    void *zConverted;
 54905    zConverted = utf8ToUnicode(zFilename);
 54906    /* caller will handle out of memory */
 54907    return zConverted;
 54908  }
 54909  /*
 54910  ** Delete the named file.
 54911  **
 54912  ** Note that windows does not allow a file to be deleted if some other
 54913  ** process has it open.  Sometimes a virus scanner or indexing program
 54914  ** will open a journal file shortly after it is created in order to do
 54915  ** whatever it does.  While this other process is holding the
 54916  ** file open, we will be unable to delete it.  To work around this
 54917  ** problem, we delay 100 milliseconds and try to delete again.  Up
 54918  ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
 54919  ** up and returning an error.
 54920  */
 54921  #define MX_DELETION_ATTEMPTS 5
 54922  static int winDelete(
 54923    unqlite_vfs *pVfs,          /* Not used on win32 */
 54924    const char *zFilename,      /* Name of file to delete */
 54925    int syncDir                 /* Not used on win32 */
 54926  ){
 54927    int cnt = 0;
 54928    DWORD rc;
 54929    DWORD error = 0;
 54930    void *zConverted;
 54931    zConverted = convertUtf8Filename(zFilename);
 54932    if( zConverted==0 ){
 54933  	   SXUNUSED(pVfs);
 54934  	   SXUNUSED(syncDir);
 54935      return UNQLITE_NOMEM;
 54936    }
 54937    do{
 54938  	  DeleteFileW((LPCWSTR)zConverted);
 54939    }while(   (   ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES)
 54940  	  || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
 54941  	  && (++cnt < MX_DELETION_ATTEMPTS)
 54942  	  && (Sleep(100), 1)
 54943  	  );
 54944  	HeapFree(GetProcessHeap(),0,zConverted);
 54945   
 54946    return (   (rc == INVALID_FILE_ATTRIBUTES) 
 54947            && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR;
 54948  }
 54949  /*
 54950  ** Check the existance and status of a file.
 54951  */
 54952  static int winAccess(
 54953    unqlite_vfs *pVfs,         /* Not used  */
 54954    const char *zFilename,     /* Name of file to check */
 54955    int flags,                 /* Type of test to make on this file */
 54956    int *pResOut               /* OUT: Result */
 54957  ){
 54958    WIN32_FILE_ATTRIBUTE_DATA sAttrData;
 54959    DWORD attr;
 54960    int rc = 0;
 54961    void *zConverted;
 54962    SXUNUSED(pVfs);
 54963  
 54964    zConverted = convertUtf8Filename(zFilename);
 54965    if( zConverted==0 ){
 54966      return UNQLITE_NOMEM;
 54967    }
 54968    SyZero(&sAttrData,sizeof(sAttrData));
 54969    if( GetFileAttributesExW((WCHAR*)zConverted,
 54970  	  GetFileExInfoStandard, 
 54971  	  &sAttrData) ){
 54972        /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file
 54973        ** as if it does not exist.
 54974        */
 54975        if(    flags==UNQLITE_ACCESS_EXISTS
 54976            && sAttrData.nFileSizeHigh==0 
 54977            && sAttrData.nFileSizeLow==0 ){
 54978          attr = INVALID_FILE_ATTRIBUTES;
 54979        }else{
 54980          attr = sAttrData.dwFileAttributes;
 54981        }
 54982      }else{
 54983        if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
 54984          HeapFree(GetProcessHeap(),0,zConverted);
 54985          return UNQLITE_IOERR;
 54986        }else{
 54987          attr = INVALID_FILE_ATTRIBUTES;
 54988        }
 54989      }
 54990    HeapFree(GetProcessHeap(),0,zConverted);
 54991    switch( flags ){
 54992       case UNQLITE_ACCESS_READWRITE:
 54993        rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
 54994        break;
 54995      case UNQLITE_ACCESS_READ:
 54996      case UNQLITE_ACCESS_EXISTS:
 54997  	default:
 54998        rc = attr!=INVALID_FILE_ATTRIBUTES;
 54999        break;
 55000    }
 55001    *pResOut = rc;
 55002    return UNQLITE_OK;
 55003  }
 55004  /*
 55005  ** Turn a relative pathname into a full pathname.  Write the full
 55006  ** pathname into zOut[].  zOut[] will be at least pVfs->mxPathname
 55007  ** bytes in size.
 55008  */
 55009  static int winFullPathname(
 55010    unqlite_vfs *pVfs,            /* Pointer to vfs object */
 55011    const char *zRelative,        /* Possibly relative input path */
 55012    int nFull,                    /* Size of output buffer in bytes */
 55013    char *zFull                   /* Output buffer */
 55014  ){
 55015    int nByte;
 55016    void *zConverted;
 55017    WCHAR *zTemp;
 55018    char *zOut;
 55019    SXUNUSED(nFull);
 55020    zConverted = convertUtf8Filename(zRelative);
 55021    if( zConverted == 0 ){
 55022  	  return UNQLITE_NOMEM;
 55023    }
 55024    nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
 55025    zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) );
 55026    if( zTemp==0 ){
 55027  	  HeapFree(GetProcessHeap(),0,zConverted);
 55028  	  return UNQLITE_NOMEM;
 55029    }
 55030    GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
 55031    HeapFree(GetProcessHeap(),0,zConverted);
 55032    zOut = unicodeToUtf8(zTemp);
 55033    HeapFree(GetProcessHeap(),0,zTemp);
 55034    if( zOut == 0 ){
 55035      return UNQLITE_NOMEM;
 55036    }
 55037    Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0);
 55038    HeapFree(GetProcessHeap(),0,zOut);
 55039    return UNQLITE_OK;
 55040  }
 55041  /*
 55042  ** Get the sector size of the device used to store
 55043  ** file.
 55044  */
 55045  static int getSectorSize(
 55046      unqlite_vfs *pVfs,
 55047      const char *zRelative     /* UTF-8 file name */
 55048  ){
 55049    DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
 55050    char zFullpath[MAX_PATH+1];
 55051    int rc;
 55052    DWORD dwRet = 0;
 55053    DWORD dwDummy;
 55054    /*
 55055    ** We need to get the full path name of the file
 55056    ** to get the drive letter to look up the sector
 55057    ** size.
 55058    */
 55059    rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
 55060    if( rc == UNQLITE_OK )
 55061    {
 55062      void *zConverted = convertUtf8Filename(zFullpath);
 55063      if( zConverted ){
 55064          /* trim path to just drive reference */
 55065          WCHAR *p = (WCHAR *)zConverted;
 55066          for(;*p;p++){
 55067            if( *p == '\\' ){
 55068              *p = '\0';
 55069              break;
 55070            }
 55071          }
 55072          dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
 55073                                    &dwDummy,
 55074                                    &bytesPerSector,
 55075                                    &dwDummy,
 55076                                    &dwDummy);
 55077  		 HeapFree(GetProcessHeap(),0,zConverted);
 55078  	}
 55079      if( !dwRet ){
 55080        bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
 55081      }
 55082    }
 55083    return (int) bytesPerSector; 
 55084  }
 55085  /*
 55086  ** Sleep for a little while.  Return the amount of time slept.
 55087  */
 55088  static int winSleep(unqlite_vfs *pVfs, int microsec){
 55089    Sleep((microsec+999)/1000);
 55090    SXUNUSED(pVfs);
 55091    return ((microsec+999)/1000)*1000;
 55092  }
 55093  /*
 55094   * Export the current system time.
 55095   */
 55096  static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
 55097  {
 55098  	SYSTEMTIME sSys;
 55099  	SXUNUSED(pVfs);
 55100  	GetSystemTime(&sSys);
 55101  	SYSTEMTIME_TO_SYTM(&sSys,pOut);
 55102  	return UNQLITE_OK;
 55103  }
 55104  /*
 55105  ** The idea is that this function works like a combination of
 55106  ** GetLastError() and FormatMessage() on windows (or errno and
 55107  ** strerror_r() on unix). After an error is returned by an OS
 55108  ** function, UnQLite calls this function with zBuf pointing to
 55109  ** a buffer of nBuf bytes. The OS layer should populate the
 55110  ** buffer with a nul-terminated UTF-8 encoded error message
 55111  ** describing the last IO error to have occurred within the calling
 55112  ** thread.
 55113  **
 55114  ** If the error message is too large for the supplied buffer,
 55115  ** it should be truncated. The return value of xGetLastError
 55116  ** is zero if the error message fits in the buffer, or non-zero
 55117  ** otherwise (if the message was truncated). If non-zero is returned,
 55118  ** then it is not necessary to include the nul-terminator character
 55119  ** in the output buffer.
 55120  */
 55121  static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf)
 55122  {
 55123    /* FormatMessage returns 0 on failure.  Otherwise it
 55124    ** returns the number of TCHARs written to the output
 55125    ** buffer, excluding the terminating null char.
 55126    */
 55127    DWORD error = GetLastError();
 55128    WCHAR *zTempWide = 0;
 55129    DWORD dwLen;
 55130    char *zOut = 0;
 55131  
 55132    SXUNUSED(pVfs);
 55133    dwLen = FormatMessageW(
 55134  	  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 55135  	  0,
 55136  	  error,
 55137  	  0,
 55138  	  (LPWSTR) &zTempWide,
 55139  	  0,
 55140  	  0
 55141  	  );
 55142      if( dwLen > 0 ){
 55143        /* allocate a buffer and convert to UTF8 */
 55144        zOut = unicodeToUtf8(zTempWide);
 55145        /* free the system buffer allocated by FormatMessage */
 55146        LocalFree(zTempWide);
 55147      }
 55148  	if( 0 == dwLen ){
 55149  		Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1);
 55150  	}else{
 55151  		/* copy a maximum of nBuf chars to output buffer */
 55152  		Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */);
 55153  		/* free the UTF8 buffer */
 55154  		HeapFree(GetProcessHeap(),0,zOut);
 55155  	}
 55156    return 0;
 55157  }
 55158  /*
 55159  ** Open a file.
 55160  */
 55161  static int winOpen(
 55162    unqlite_vfs *pVfs,        /* Not used */
 55163    const char *zName,        /* Name of the file (UTF-8) */
 55164    unqlite_file *id,         /* Write the UnQLite file handle here */
 55165    unsigned int flags                /* Open mode flags */
 55166  ){
 55167    HANDLE h;
 55168    DWORD dwDesiredAccess;
 55169    DWORD dwShareMode;
 55170    DWORD dwCreationDisposition;
 55171    DWORD dwFlagsAndAttributes = 0;
 55172    winFile *pFile = (winFile*)id;
 55173    void *zConverted;              /* Filename in OS encoding */
 55174    const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
 55175    int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
 55176    int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
 55177    int isCreate     = (flags & UNQLITE_OPEN_CREATE);
 55178    int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
 55179  
 55180    pFile->h = INVALID_HANDLE_VALUE;
 55181    /* Convert the filename to the system encoding. */
 55182    zConverted = convertUtf8Filename(zUtf8Name);
 55183    if( zConverted==0 ){
 55184      return UNQLITE_NOMEM;
 55185    }
 55186    if( isReadWrite ){
 55187      dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
 55188    }else{
 55189      dwDesiredAccess = GENERIC_READ;
 55190    }
 55191    /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is 
 55192    ** created.
 55193    */
 55194    if( isExclusive ){
 55195      /* Creates a new file, only if it does not already exist. */
 55196      /* If the file exists, it fails. */
 55197      dwCreationDisposition = CREATE_NEW;
 55198    }else if( isCreate ){
 55199      /* Open existing file, or create if it doesn't exist */
 55200      dwCreationDisposition = OPEN_ALWAYS;
 55201    }else{
 55202      /* Opens a file, only if it exists. */
 55203      dwCreationDisposition = OPEN_EXISTING;
 55204    }
 55205  
 55206    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
 55207  
 55208    if( isDelete ){
 55209      dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
 55210                                 | FILE_ATTRIBUTE_HIDDEN
 55211                                 | FILE_FLAG_DELETE_ON_CLOSE;
 55212    }else{
 55213      dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
 55214    }
 55215    h = CreateFileW((WCHAR*)zConverted,
 55216         dwDesiredAccess,
 55217         dwShareMode,
 55218         NULL,
 55219         dwCreationDisposition,
 55220         dwFlagsAndAttributes,
 55221         NULL
 55222      );
 55223    if( h==INVALID_HANDLE_VALUE ){
 55224      pFile->lastErrno = GetLastError();
 55225      HeapFree(GetProcessHeap(),0,zConverted);
 55226  	return UNQLITE_IOERR;
 55227    }
 55228    SyZero(pFile,sizeof(*pFile));
 55229    pFile->pMethod = &winIoMethod;
 55230    pFile->h = h;
 55231    pFile->lastErrno = NO_ERROR;
 55232    pFile->pVfs = pVfs;
 55233    pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
 55234    HeapFree(GetProcessHeap(),0,zConverted);
 55235    return UNQLITE_OK;
 55236  }
 55237  /*
 55238   * Export the Windows Vfs.
 55239   */
 55240  UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
 55241  {
 55242  	static const unqlite_vfs sWinvfs = {
 55243  		"Windows",           /* Vfs name */
 55244  		1,                   /* Vfs structure version */
 55245  		sizeof(winFile),     /* szOsFile */
 55246  		MAX_PATH,            /* mxPathName */
 55247  		winOpen,             /* xOpen */
 55248  		winDelete,           /* xDelete */
 55249  		winAccess,           /* xAccess */
 55250  		winFullPathname,     /* xFullPathname */
 55251  		0,                   /* xTmp */
 55252  		winSleep,            /* xSleep */
 55253  		winCurrentTime,      /* xCurrentTime */
 55254  		winGetLastError,     /* xGetLastError */
 55255  	};
 55256  	return &sWinvfs;
 55257  }
 55258  #endif /* __WINNT__ */
 55259  /*
 55260   * ----------------------------------------------------------
 55261   * File: pager.c
 55262   * MD5: 57ff77347402fbf6892af589ff8a5df7
 55263   * ----------------------------------------------------------
 55264   */
 55265  /*
 55266   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 55267   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 55268   * Version 1.1.6
 55269   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 55270   * please contact Symisc Systems via:
 55271   *       legal@symisc.net
 55272   *       licensing@symisc.net
 55273   *       contact@symisc.net
 55274   * or visit:
 55275   *      http://unqlite.org/licensing.html
 55276   */
 55277   /* $SymiscID: pager.c v1.1 Win7 2012-11-29 03:46 stable <chm@symisc.net> $ */
 55278  #ifndef UNQLITE_AMALGAMATION
 55279  #include "unqliteInt.h"
 55280  #endif
 55281  /*
 55282  ** This file implements the pager and the transaction manager for UnQLite (Mostly inspired from the SQLite3 Source tree).
 55283  **
 55284  ** The Pager.eState variable stores the current 'state' of a pager. A
 55285  ** pager may be in any one of the seven states shown in the following
 55286  ** state diagram.
 55287  **
 55288  **                            OPEN <------+------+
 55289  **                              |         |      |
 55290  **                              V         |      |
 55291  **               +---------> READER-------+      |
 55292  **               |              |                |
 55293  **               |              V                |
 55294  **               |<-------WRITER_LOCKED--------->| 
 55295  **               |              |                |  
 55296  **               |              V                |
 55297  **               |<------WRITER_CACHEMOD-------->|
 55298  **               |              |                |
 55299  **               |              V                |
 55300  **               |<-------WRITER_DBMOD---------->|
 55301  **               |              |                |
 55302  **               |              V                |
 55303  **               +<------WRITER_FINISHED-------->+
 55304  ** 
 55305  **  OPEN:
 55306  **
 55307  **    The pager starts up in this state. Nothing is guaranteed in this
 55308  **    state - the file may or may not be locked and the database size is
 55309  **    unknown. The database may not be read or written.
 55310  **
 55311  **    * No read or write transaction is active.
 55312  **    * Any lock, or no lock at all, may be held on the database file.
 55313  **    * The dbSize and dbOrigSize variables may not be trusted.
 55314  **
 55315  **  READER:
 55316  **
 55317  **    In this state all the requirements for reading the database in 
 55318  **    rollback mode are met. Unless the pager is (or recently
 55319  **    was) in exclusive-locking mode, a user-level read transaction is 
 55320  **    open. The database size is known in this state.
 55321  ** 
 55322  **    * A read transaction may be active (but a write-transaction cannot).
 55323  **    * A SHARED or greater lock is held on the database file.
 55324  **    * The dbSize variable may be trusted (even if a user-level read 
 55325  **      transaction is not active). The dbOrigSize variables
 55326  **      may not be trusted at this point.
 55327  **    * Even if a read-transaction is not open, it is guaranteed that 
 55328  **      there is no hot-journal in the file-system.
 55329  **
 55330  **  WRITER_LOCKED:
 55331  **
 55332  **    The pager moves to this state from READER when a write-transaction
 55333  **    is first opened on the database. In WRITER_LOCKED state, all locks 
 55334  **    required to start a write-transaction are held, but no actual 
 55335  **    modifications to the cache or database have taken place.
 55336  **
 55337  **    In rollback mode, a RESERVED or (if the transaction was opened with 
 55338  **    EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when
 55339  **    moving to this state, but the journal file is not written to or opened 
 55340  **    to in this state. If the transaction is committed or rolled back while 
 55341  **    in WRITER_LOCKED state, all that is required is to unlock the database 
 55342  **    file.
 55343  **
 55344  **    * A write transaction is active.
 55345  **    * If the connection is open in rollback-mode, a RESERVED or greater 
 55346  **      lock is held on the database file.
 55347  **    * The dbSize and dbOrigSize variables are all valid.
 55348  **    * The contents of the pager cache have not been modified.
 55349  **    * The journal file may or may not be open.
 55350  **    * Nothing (not even the first header) has been written to the journal.
 55351  **
 55352  **  WRITER_CACHEMOD:
 55353  **
 55354  **    A pager moves from WRITER_LOCKED state to this state when a page is
 55355  **    first modified by the upper layer. In rollback mode the journal file
 55356  **    is opened (if it is not already open) and a header written to the
 55357  **    start of it. The database file on disk has not been modified.
 55358  **
 55359  **    * A write transaction is active.
 55360  **    * A RESERVED or greater lock is held on the database file.
 55361  **    * The journal file is open and the first header has been written 
 55362  **      to it, but the header has not been synced to disk.
 55363  **    * The contents of the page cache have been modified.
 55364  **
 55365  **  WRITER_DBMOD:
 55366  **
 55367  **    The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
 55368  **    when it modifies the contents of the database file.
 55369  **
 55370  **    * A write transaction is active.
 55371  **    * An EXCLUSIVE or greater lock is held on the database file.
 55372  **    * The journal file is open and the first header has been written 
 55373  **      and synced to disk.
 55374  **    * The contents of the page cache have been modified (and possibly
 55375  **      written to disk).
 55376  **
 55377  **  WRITER_FINISHED:
 55378  **
 55379  **    A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
 55380  **    state after the entire transaction has been successfully written into the
 55381  **    database file. In this state the transaction may be committed simply
 55382  **    by finalizing the journal file. Once in WRITER_FINISHED state, it is 
 55383  **    not possible to modify the database further. At this point, the upper 
 55384  **    layer must either commit or rollback the transaction.
 55385  **
 55386  **    * A write transaction is active.
 55387  **    * An EXCLUSIVE or greater lock is held on the database file.
 55388  **    * All writing and syncing of journal and database data has finished.
 55389  **      If no error occured, all that remains is to finalize the journal to
 55390  **      commit the transaction. If an error did occur, the caller will need
 55391  **      to rollback the transaction. 
 55392  **  
 55393  **
 55394  */
 55395  #define PAGER_OPEN                  0
 55396  #define PAGER_READER                1
 55397  #define PAGER_WRITER_LOCKED         2
 55398  #define PAGER_WRITER_CACHEMOD       3
 55399  #define PAGER_WRITER_DBMOD          4
 55400  #define PAGER_WRITER_FINISHED       5
 55401  /*
 55402  ** Journal files begin with the following magic string.  The data
 55403  ** was obtained from /dev/random.  It is used only as a sanity check.
 55404  **
 55405  ** NOTE: These values must be different from the one used by SQLite3
 55406  ** to avoid journal file collision.
 55407  **
 55408  */
 55409  static const unsigned char aJournalMagic[] = {
 55410    0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f,
 55411  };
 55412  /*
 55413  ** The journal header size for this pager. This is usually the same 
 55414  ** size as a single disk sector. See also setSectorSize().
 55415  */
 55416  #define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize)
 55417  /*
 55418   * Database page handle.
 55419   * Each raw disk page is represented in memory by an instance
 55420   * of the following structure.
 55421   */
 55422  typedef struct Page Page;
 55423  struct Page {
 55424    /* Must correspond to unqlite_page */
 55425    unsigned char *zData;           /* Content of this page */
 55426    void *pUserData;                /* Extra content */
 55427    pgno pgno;                      /* Page number for this page */
 55428    /**********************************************************************
 55429    ** Elements above are public.  All that follows is private to pcache.c
 55430    ** and should not be accessed by other modules.
 55431    */
 55432    Pager *pPager;                 /* The pager this page is part of */
 55433    int flags;                     /* Page flags defined below */
 55434    int nRef;                      /* Number of users of this page */
 55435    Page *pNext, *pPrev;    /* A list of all pages */
 55436    Page *pDirtyNext;             /* Next element in list of dirty pages */
 55437    Page *pDirtyPrev;             /* Previous element in list of dirty pages */
 55438    Page *pNextCollide,*pPrevCollide; /* Collission chain */
 55439    Page *pNextHot,*pPrevHot;    /* Hot dirty pages chain */
 55440  };
 55441  /* Bit values for Page.flags */
 55442  #define PAGE_DIRTY             0x002  /* Page has changed */
 55443  #define PAGE_NEED_SYNC         0x004  /* fsync the rollback journal before
 55444                                         ** writing this page to the database */
 55445  #define PAGE_DONT_WRITE        0x008  /* Dont write page content to disk */
 55446  #define PAGE_NEED_READ         0x010  /* Content is unread */
 55447  #define PAGE_IN_JOURNAL        0x020  /* Page written to the journal */
 55448  #define PAGE_HOT_DIRTY         0x040  /* Hot dirty page */
 55449  #define PAGE_DONT_MAKE_HOT     0x080  /* Dont make this page Hot. In other words,
 55450  									   * do not link it to the hot dirty list.
 55451  									   */
 55452  /*
 55453   * Each active database pager is represented by an instance of
 55454   * the following structure.
 55455   */
 55456  struct Pager
 55457  {
 55458    SyMemBackend *pAllocator;      /* Memory backend */
 55459    unqlite *pDb;                  /* DB handle that own this instance */
 55460    unqlite_kv_engine *pEngine;    /* Underlying KV storage engine */
 55461    char *zFilename;               /* Name of the database file */
 55462    char *zJournal;                /* Name of the journal file */
 55463    unqlite_vfs *pVfs;             /* Underlying virtual file system */
 55464    unqlite_file *pfd,*pjfd;       /* File descriptors for database and journal */
 55465    pgno dbSize;                   /* Number of pages in the file */
 55466    pgno dbOrigSize;               /* dbSize before the current change */
 55467    sxi64 dbByteSize;              /* Database size in bytes */
 55468    void *pMmap;                   /* Read-only Memory view (mmap) of the whole file if requested (UNQLITE_OPEN_MMAP). */
 55469    sxu32 nRec;                    /* Number of pages written to the journal */
 55470    SyPRNGCtx sPrng;               /* PRNG Context */
 55471    sxu32 cksumInit;               /* Quasi-random value added to every checksum */
 55472    sxu32 iOpenFlags;              /* Flag passed to unqlite_open() after processing */
 55473    sxi64 iJournalOfft;            /* Journal offset we are reading from */
 55474    int (*xBusyHandler)(void *);   /* Busy handler */
 55475    void *pBusyHandlerArg;         /* First arg to xBusyHandler() */
 55476    void (*xPageUnpin)(void *);    /* Page Unpin callback */
 55477    void (*xPageReload)(void *);   /* Page Reload callback */
 55478    Bitvec *pVec;                  /* Bitmap */
 55479    Page *pHeader;                 /* Page one of the database (Unqlite header) */
 55480    Sytm tmCreate;                 /* Database creation time */
 55481    SyString sKv;                  /* Underlying Key/Value storage engine name */
 55482    int iState;                    /* Pager state */
 55483    int iLock;                     /* Lock state */
 55484    sxi32 iFlags;                  /* Control flags (see below) */
 55485    int is_mem;                    /* True for an in-memory database */
 55486    int is_rdonly;                 /* True for a read-only database */
 55487    int no_jrnl;                   /* TRUE to omit journaling */
 55488    int iPageSize;                 /* Page size in bytes (default 4K) */
 55489    int iSectorSize;               /* Size of a single sector on disk */
 55490    unsigned char *zTmpPage;       /* Temporary page */
 55491    Page *pFirstDirty;             /* First dirty pages */
 55492    Page *pDirty;                  /* Transient list of dirty pages */
 55493    Page *pAll;                    /* List of all pages */
 55494    Page *pHotDirty;               /* List of hot dirty pages */
 55495    Page *pFirstHot;               /* First hot dirty page */
 55496    sxu32 nHot;                    /* Total number of hot dirty pages */
 55497    Page **apHash;                 /* Page table */
 55498    sxu32 nSize;                   /* apHash[] size: Must be a power of two  */
 55499    sxu32 nPage;                   /* Total number of page loaded in memory */
 55500    sxu32 nCacheMax;               /* Maximum page to cache*/
 55501  };
 55502  /* Control flags */
 55503  #define PAGER_CTRL_COMMIT_ERR   0x001 /* Commit error */
 55504  #define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */ 
 55505  /*
 55506  ** Read a 32-bit integer from the given file descriptor. 
 55507  ** All values are stored on disk as big-endian.
 55508  */
 55509  static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft)
 55510  {
 55511  	unsigned char zBuf[4];
 55512  	int rc;
 55513  	rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
 55514  	if( rc != UNQLITE_OK ){
 55515  		return rc;
 55516  	}
 55517  	SyBigEndianUnpack32(zBuf,pOut);
 55518  	return UNQLITE_OK;
 55519  }
 55520  /*
 55521  ** Read a 64-bit integer from the given file descriptor. 
 55522  ** All values are stored on disk as big-endian.
 55523  */
 55524  static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft)
 55525  {
 55526  	unsigned char zBuf[8];
 55527  	int rc;
 55528  	rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
 55529  	if( rc != UNQLITE_OK ){
 55530  		return rc;
 55531  	}
 55532  	SyBigEndianUnpack64(zBuf,pOut);
 55533  	return UNQLITE_OK;
 55534  }
 55535  /*
 55536  ** Write a 32-bit integer into the given file descriptor.
 55537  */
 55538  static int WriteInt32(unqlite_file *pFd,sxu32 iNum,sxi64 iOfft)
 55539  {
 55540  	unsigned char zBuf[4];
 55541  	int rc;
 55542  	SyBigEndianPack32(zBuf,iNum);
 55543  	rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
 55544  	return rc;
 55545  }
 55546  /*
 55547  ** Write a 64-bit integer into the given file descriptor.
 55548  */
 55549  static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft)
 55550  {
 55551  	unsigned char zBuf[8];
 55552  	int rc;
 55553  	SyBigEndianPack64(zBuf,iNum);
 55554  	rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
 55555  	return rc;
 55556  }
 55557  /*
 55558  ** The maximum allowed sector size. 64KiB. If the xSectorsize() method 
 55559  ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
 55560  ** This could conceivably cause corruption following a power failure on
 55561  ** such a system. This is currently an undocumented limit.
 55562  */
 55563  #define MAX_SECTOR_SIZE 0x10000
 55564  /*
 55565  ** Get the size of a single sector on disk.
 55566  ** The sector size will be used used  to determine the size
 55567  ** and alignment of journal header and within created journal files.
 55568  **
 55569  ** The default sector size is set to 512.
 55570  */
 55571  static int GetSectorSize(unqlite_file *pFd)
 55572  {
 55573  	int iSectorSize = UNQLITE_DEFAULT_SECTOR_SIZE;
 55574  	if( pFd ){
 55575  		iSectorSize = unqliteOsSectorSize(pFd);
 55576  		if( iSectorSize < 32 ){
 55577  			iSectorSize = 512;
 55578  		}
 55579  		if( iSectorSize > MAX_SECTOR_SIZE ){
 55580  			iSectorSize = MAX_SECTOR_SIZE;
 55581  		}
 55582  	}
 55583  	return iSectorSize;
 55584  }
 55585  /* Hash function for page number  */
 55586  #define PAGE_HASH(PNUM) (PNUM)
 55587  /*
 55588   * Fetch a page from the cache.
 55589   */
 55590  static Page * pager_fetch_page(Pager *pPager,pgno page_num)
 55591  {
 55592  	Page *pEntry;
 55593  	if( pPager->nPage < 1 ){
 55594  		/* Don't bother hashing */
 55595  		return 0;
 55596  	}
 55597  	/* Perform the lookup */
 55598  	pEntry = pPager->apHash[PAGE_HASH(page_num) & (pPager->nSize - 1)];
 55599  	for(;;){
 55600  		if( pEntry == 0 ){
 55601  			break;
 55602  		}
 55603  		if( pEntry->pgno == page_num ){
 55604  			return pEntry;
 55605  		}
 55606  		/* Point to the next entry in the colission chain */
 55607  		pEntry = pEntry->pNextCollide;
 55608  	}
 55609  	/* No such page */
 55610  	return 0;
 55611  }
 55612  /*
 55613   * Allocate and initialize a new page.
 55614   */
 55615  static Page * pager_alloc_page(Pager *pPager,pgno num_page)
 55616  {
 55617  	Page *pNew;
 55618  	
 55619  	pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize);
 55620  	if( pNew == 0 ){
 55621  		return 0;
 55622  	}
 55623  	/* Zero the structure */
 55624  	SyZero(pNew,sizeof(Page)+pPager->iPageSize);
 55625  	/* Page data */
 55626  	pNew->zData = (unsigned char *)&pNew[1];
 55627  	/* Fill in the structure */
 55628  	pNew->pPager = pPager;
 55629  	pNew->nRef = 1;
 55630  	pNew->pgno = num_page;
 55631  	return pNew;
 55632  }
 55633  /*
 55634   * Increment the reference count of a given page.
 55635   */
 55636  static void page_ref(Page *pPage)
 55637  {
 55638  	pPage->nRef++;
 55639  }
 55640  /*
 55641   * Release an in-memory page after its reference count reach zero.
 55642   */
 55643  static int pager_release_page(Pager *pPager,Page *pPage)
 55644  {
 55645  	int rc = UNQLITE_OK;
 55646  	if( !(pPage->flags & PAGE_DIRTY)){
 55647  		/* Invoke the unpin callback if available */
 55648  		if( pPager->xPageUnpin && pPage->pUserData ){
 55649  			pPager->xPageUnpin(pPage->pUserData);
 55650  		}
 55651  		pPage->pUserData = 0;
 55652  		SyMemBackendPoolFree(pPager->pAllocator,pPage);
 55653  	}else{
 55654  		/* Dirty page, it will be released later when a dirty commit
 55655  		 * or the final commit have been applied.
 55656  		 */
 55657  		rc = UNQLITE_LOCKED;
 55658  	}
 55659  	return rc;
 55660  }
 55661  /* Forward declaration */
 55662  static int pager_unlink_page(Pager *pPager,Page *pPage);
 55663  /*
 55664   * Decrement the reference count of a given page.
 55665   */
 55666  static void page_unref(Page *pPage)
 55667  {
 55668  	pPage->nRef--;
 55669  	if( pPage->nRef < 1	){
 55670  		Pager *pPager = pPage->pPager;
 55671  		if( !(pPage->flags & PAGE_DIRTY)  ){
 55672  			pager_unlink_page(pPager,pPage);
 55673  			/* Release the page */
 55674  			pager_release_page(pPager,pPage);
 55675  		}else{
 55676  			if( pPage->flags & PAGE_DONT_MAKE_HOT ){
 55677  				/* Do not add this page to the hot dirty list */
 55678  				return;
 55679  			}
 55680  			if( !(pPage->flags & PAGE_HOT_DIRTY) ){
 55681  				/* Add to the hot dirty list */
 55682  				pPage->pPrevHot = 0;
 55683  				if( pPager->pFirstHot == 0 ){
 55684  					pPager->pFirstHot = pPager->pHotDirty = pPage;
 55685  				}else{
 55686  					pPage->pNextHot = pPager->pHotDirty;
 55687  					if( pPager->pHotDirty ){
 55688  						pPager->pHotDirty->pPrevHot = pPage;
 55689  					}
 55690  					pPager->pHotDirty = pPage;
 55691  				}
 55692  				pPager->nHot++;
 55693  				pPage->flags |= PAGE_HOT_DIRTY;
 55694  			}
 55695  		}
 55696  	}
 55697  }
 55698  /*
 55699   * Link a freshly created page to the list of active page.
 55700   */
 55701  static int pager_link_page(Pager *pPager,Page *pPage)
 55702  {
 55703  	sxu32 nBucket;
 55704  	/* Install in the corresponding bucket */
 55705  	nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
 55706  	pPage->pNextCollide = pPager->apHash[nBucket];
 55707  	if( pPager->apHash[nBucket] ){
 55708  		pPager->apHash[nBucket]->pPrevCollide = pPage;
 55709  	}
 55710  	pPager->apHash[nBucket] = pPage;
 55711  	/* Link to the list of active pages */
 55712  	MACRO_LD_PUSH(pPager->pAll,pPage);
 55713  	pPager->nPage++;
 55714  	if( (pPager->nPage >= pPager->nSize * 4)  && pPager->nPage < 100000 ){
 55715  		/* Grow the hashtable */
 55716  		sxu32 nNewSize = pPager->nSize << 1;
 55717  		Page *pEntry,**apNew;
 55718  		sxu32 n;
 55719  		apNew = (Page **)SyMemBackendAlloc(pPager->pAllocator, nNewSize * sizeof(Page *));
 55720  		if( apNew ){
 55721  			sxu32 iBucket;
 55722  			/* Zero the new table */
 55723  			SyZero((void *)apNew, nNewSize * sizeof(Page *));
 55724  			/* Rehash all entries */
 55725  			n = 0;
 55726  			pEntry = pPager->pAll;
 55727  			for(;;){
 55728  				/* Loop one */
 55729  				if( n >= pPager->nPage ){
 55730  					break;
 55731  				}
 55732  				pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 55733  				/* Install in the new bucket */
 55734  				iBucket = PAGE_HASH(pEntry->pgno) & (nNewSize - 1);
 55735  				pEntry->pNextCollide = apNew[iBucket];
 55736  				if( apNew[iBucket] ){
 55737  					apNew[iBucket]->pPrevCollide = pEntry;
 55738  				}
 55739  				apNew[iBucket] = pEntry;
 55740  				/* Point to the next entry */
 55741  				pEntry = pEntry->pNext;
 55742  				n++;
 55743  			}
 55744  			/* Release the old table and reflect the change */
 55745  			SyMemBackendFree(pPager->pAllocator,(void *)pPager->apHash);
 55746  			pPager->apHash = apNew;
 55747  			pPager->nSize  = nNewSize;
 55748  		}
 55749  	}
 55750  	return UNQLITE_OK;
 55751  }
 55752  /*
 55753   * Unlink a page from the list of active pages.
 55754   */
 55755  static int pager_unlink_page(Pager *pPager,Page *pPage)
 55756  {
 55757  	if( pPage->pNextCollide ){
 55758  		pPage->pNextCollide->pPrevCollide = pPage->pPrevCollide;
 55759  	}
 55760  	if( pPage->pPrevCollide ){
 55761  		pPage->pPrevCollide->pNextCollide = pPage->pNextCollide;
 55762  	}else{
 55763  		sxu32 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
 55764  		pPager->apHash[nBucket] = pPage->pNextCollide;
 55765  	}
 55766  	MACRO_LD_REMOVE(pPager->pAll,pPage);
 55767  	pPager->nPage--;
 55768  	return UNQLITE_OK;
 55769  }
 55770  /*
 55771   * Update the content of a cached page.
 55772   */
 55773  static int pager_fill_page(Pager *pPager,pgno iNum,void *pContents)
 55774  {
 55775  	Page *pPage;
 55776  	/* Fetch the page from the catch */
 55777  	pPage = pager_fetch_page(pPager,iNum);
 55778  	if( pPage == 0 ){
 55779  		return SXERR_NOTFOUND;
 55780  	}
 55781  	/* Reflect the change */
 55782  	SyMemcpy(pContents,pPage->zData,pPager->iPageSize);
 55783  
 55784  	return UNQLITE_OK;
 55785  }
 55786  /*
 55787   * Read the content of a page from disk.
 55788   */
 55789  static int pager_get_page_contents(Pager *pPager,Page *pPage,int noContent)
 55790  {
 55791  	int rc = UNQLITE_OK;
 55792  	if( pPager->is_mem || noContent || pPage->pgno >= pPager->dbSize ){
 55793  		/* Do not bother reading, zero the page contents only */
 55794  		SyZero(pPage->zData,pPager->iPageSize);
 55795  		return UNQLITE_OK;
 55796  	}
 55797  	if( (pPager->iOpenFlags & UNQLITE_OPEN_MMAP) && (pPager->pMmap /* Paranoid edition */) ){
 55798  		unsigned char *zMap = (unsigned char *)pPager->pMmap;
 55799  		pPage->zData = &zMap[pPage->pgno * pPager->iPageSize];
 55800  	}else{
 55801  		/* Read content */
 55802  		rc = unqliteOsRead(pPager->pfd,pPage->zData,pPager->iPageSize,pPage->pgno * pPager->iPageSize);
 55803  	}
 55804  	return rc;
 55805  }
 55806  /*
 55807   * Add a page to the dirty list.
 55808   */
 55809  static void pager_page_to_dirty_list(Pager *pPager,Page *pPage)
 55810  {
 55811  	if( pPage->flags & PAGE_DIRTY ){
 55812  		/* Already set */
 55813  		return;
 55814  	}
 55815  	/* Mark the page as dirty */
 55816  	pPage->flags |= PAGE_DIRTY|PAGE_NEED_SYNC|PAGE_IN_JOURNAL;
 55817  	/* Link to the list */
 55818  	pPage->pDirtyPrev = 0;
 55819  	pPage->pDirtyNext = pPager->pDirty;
 55820  	if( pPager->pDirty ){
 55821  		pPager->pDirty->pDirtyPrev = pPage;
 55822  	}
 55823  	pPager->pDirty = pPage;
 55824  	if( pPager->pFirstDirty == 0 ){
 55825  		pPager->pFirstDirty = pPage;
 55826  	}
 55827  }
 55828  /*
 55829   * Merge sort.
 55830   * The merge sort implementation is based on the one used by
 55831   * the PH7 Embeddable PHP Engine (http://ph7.symisc.net/).
 55832   */
 55833  /*
 55834  ** Inputs:
 55835  **   a:       A sorted, null-terminated linked list.  (May be null).
 55836  **   b:       A sorted, null-terminated linked list.  (May be null).
 55837  **   cmp:     A pointer to the comparison function.
 55838  **
 55839  ** Return Value:
 55840  **   A pointer to the head of a sorted list containing the elements
 55841  **   of both a and b.
 55842  **
 55843  ** Side effects:
 55844  **   The "next", "prev" pointers for elements in the lists a and b are
 55845  **   changed.
 55846  */
 55847  static Page * page_merge_dirty(Page *pA, Page *pB)
 55848  {
 55849  	Page result, *pTail;
 55850      /* Prevent compiler warning */
 55851  	result.pDirtyNext = result.pDirtyPrev = 0;
 55852  	pTail = &result;
 55853  	while( pA && pB ){
 55854  		if( pA->pgno < pB->pgno ){
 55855  			pTail->pDirtyPrev = pA;
 55856  			pA->pDirtyNext = pTail;
 55857  			pTail = pA;
 55858  			pA = pA->pDirtyPrev;
 55859  		}else{
 55860  			pTail->pDirtyPrev = pB;
 55861  			pB->pDirtyNext = pTail;
 55862  			pTail = pB;
 55863  			pB = pB->pDirtyPrev;
 55864  		}
 55865  	}
 55866  	if( pA ){
 55867  		pTail->pDirtyPrev = pA;
 55868  		pA->pDirtyNext = pTail;
 55869  	}else if( pB ){
 55870  		pTail->pDirtyPrev = pB;
 55871  		pB->pDirtyNext = pTail;
 55872  	}else{
 55873  		pTail->pDirtyPrev = pTail->pDirtyNext = 0;
 55874  	}
 55875  	return result.pDirtyPrev;
 55876  }
 55877  /*
 55878  ** Inputs:
 55879  **   Map:       Input hashmap
 55880  **   cmp:       A comparison function.
 55881  **
 55882  ** Return Value:
 55883  **   Sorted hashmap.
 55884  **
 55885  ** Side effects:
 55886  **   The "next" pointers for elements in list are changed.
 55887  */
 55888  #define N_SORT_BUCKET  32
 55889  static Page * pager_get_dirty_pages(Pager *pPager)
 55890  {
 55891  	Page *a[N_SORT_BUCKET], *p, *pIn;
 55892  	sxu32 i;
 55893  	if( pPager->pFirstDirty == 0 ){
 55894  		/* Don't bother sorting, the list is already empty */
 55895  		return 0;
 55896  	}
 55897  	SyZero(a, sizeof(a));
 55898  	/* Point to the first inserted entry */
 55899  	pIn = pPager->pFirstDirty;
 55900  	while( pIn ){
 55901  		p = pIn;
 55902  		pIn = p->pDirtyPrev;
 55903  		p->pDirtyPrev = 0;
 55904  		for(i=0; i<N_SORT_BUCKET-1; i++){
 55905  			if( a[i]==0 ){
 55906  				a[i] = p;
 55907  				break;
 55908  			}else{
 55909  				p = page_merge_dirty(a[i], p);
 55910  				a[i] = 0;
 55911  			}
 55912  		}
 55913  		if( i==N_SORT_BUCKET-1 ){
 55914  			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
 55915  			 * But that is impossible.
 55916  			 */
 55917  			a[i] = page_merge_dirty(a[i], p);
 55918  		}
 55919  	}
 55920  	p = a[0];
 55921  	for(i=1; i<N_SORT_BUCKET; i++){
 55922  		p = page_merge_dirty(p,a[i]);
 55923  	}
 55924  	p->pDirtyNext = 0;
 55925  	return p;
 55926  }
 55927  /*
 55928   * See block comment above.
 55929   */
 55930  static Page * page_merge_hot(Page *pA, Page *pB)
 55931  {
 55932  	Page result, *pTail;
 55933      /* Prevent compiler warning */
 55934  	result.pNextHot = result.pPrevHot = 0;
 55935  	pTail = &result;
 55936  	while( pA && pB ){
 55937  		if( pA->pgno < pB->pgno ){
 55938  			pTail->pPrevHot = pA;
 55939  			pA->pNextHot = pTail;
 55940  			pTail = pA;
 55941  			pA = pA->pPrevHot;
 55942  		}else{
 55943  			pTail->pPrevHot = pB;
 55944  			pB->pNextHot = pTail;
 55945  			pTail = pB;
 55946  			pB = pB->pPrevHot;
 55947  		}
 55948  	}
 55949  	if( pA ){
 55950  		pTail->pPrevHot = pA;
 55951  		pA->pNextHot = pTail;
 55952  	}else if( pB ){
 55953  		pTail->pPrevHot = pB;
 55954  		pB->pNextHot = pTail;
 55955  	}else{
 55956  		pTail->pPrevHot = pTail->pNextHot = 0;
 55957  	}
 55958  	return result.pPrevHot;
 55959  }
 55960  /*
 55961  ** Inputs:
 55962  **   Map:       Input hashmap
 55963  **   cmp:       A comparison function.
 55964  **
 55965  ** Return Value:
 55966  **   Sorted hashmap.
 55967  **
 55968  ** Side effects:
 55969  **   The "next" pointers for elements in list are changed.
 55970  */
 55971  #define N_SORT_BUCKET  32
 55972  static Page * pager_get_hot_pages(Pager *pPager)
 55973  {
 55974  	Page *a[N_SORT_BUCKET], *p, *pIn;
 55975  	sxu32 i;
 55976  	if( pPager->pFirstHot == 0 ){
 55977  		/* Don't bother sorting, the list is already empty */
 55978  		return 0;
 55979  	}
 55980  	SyZero(a, sizeof(a));
 55981  	/* Point to the first inserted entry */
 55982  	pIn = pPager->pFirstHot;
 55983  	while( pIn ){
 55984  		p = pIn;
 55985  		pIn = p->pPrevHot;
 55986  		p->pPrevHot = 0;
 55987  		for(i=0; i<N_SORT_BUCKET-1; i++){
 55988  			if( a[i]==0 ){
 55989  				a[i] = p;
 55990  				break;
 55991  			}else{
 55992  				p = page_merge_hot(a[i], p);
 55993  				a[i] = 0;
 55994  			}
 55995  		}
 55996  		if( i==N_SORT_BUCKET-1 ){
 55997  			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
 55998  			 * But that is impossible.
 55999  			 */
 56000  			a[i] = page_merge_hot(a[i], p);
 56001  		}
 56002  	}
 56003  	p = a[0];
 56004  	for(i=1; i<N_SORT_BUCKET; i++){
 56005  		p = page_merge_hot(p,a[i]);
 56006  	}
 56007  	p->pNextHot = 0;
 56008  	return p;
 56009  }
 56010  /*
 56011  ** The format for the journal header is as follows:
 56012  ** - 8 bytes: Magic identifying journal format.
 56013  ** - 4 bytes: Number of records in journal.
 56014  ** - 4 bytes: Random number used for page hash.
 56015  ** - 8 bytes: Initial database page count.
 56016  ** - 4 bytes: Sector size used by the process that wrote this journal.
 56017  ** - 4 bytes: Database page size.
 56018  ** 
 56019  ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
 56020  */
 56021  /*
 56022  ** Open the journal file and extract its header information.
 56023  **
 56024  ** If the header is read successfully, *pNRec is set to the number of
 56025  ** page records following this header and *pDbSize is set to the size of the
 56026  ** database before the transaction began, in pages. Also, pPager->cksumInit
 56027  ** is set to the value read from the journal header. UNQLITE_OK is returned
 56028  ** in this case.
 56029  **
 56030  ** If the journal header file appears to be corrupted, UNQLITE_DONE is
 56031  ** returned and *pNRec and *PDbSize are undefined.  If JOURNAL_HDR_SZ bytes
 56032  ** cannot be read from the journal file an error code is returned.
 56033  */
 56034  static int pager_read_journal_header(
 56035    Pager *pPager,               /* Pager object */
 56036    sxu32 *pNRec,                /* OUT: Value read from the nRec field */
 56037    pgno  *pDbSize               /* OUT: Value of original database size field */
 56038  )
 56039  {
 56040  	sxu32 iPageSize,iSectorSize;
 56041  	unsigned char zMagic[8];
 56042  	sxi64 iHdrOfft;
 56043  	sxi64 iSize;
 56044  	int rc;
 56045  	/* Offset to start reading from */
 56046  	iHdrOfft = 0;
 56047  	/* Get the size of the journal */
 56048  	rc = unqliteOsFileSize(pPager->pjfd,&iSize);
 56049  	if( rc != UNQLITE_OK ){
 56050  		return UNQLITE_DONE;
 56051  	}
 56052  	/* If the journal file is too small, return UNQLITE_DONE. */
 56053  	if( 32 /* Minimum sector size */> iSize ){
 56054  		return UNQLITE_DONE;
 56055  	}
 56056  	/* Make sure we are dealing with a valid journal */
 56057  	rc = unqliteOsRead(pPager->pjfd,zMagic,sizeof(zMagic),iHdrOfft);
 56058  	if( rc != UNQLITE_OK ){
 56059  		return rc;
 56060  	}
 56061  	if( SyMemcmp(zMagic,aJournalMagic,sizeof(zMagic)) != 0 ){
 56062  		return UNQLITE_DONE;
 56063  	}
 56064  	iHdrOfft += sizeof(zMagic);
 56065  	 /* Read the first three 32-bit fields of the journal header: The nRec
 56066        ** field, the checksum-initializer and the database size at the start
 56067        ** of the transaction. Return an error code if anything goes wrong.
 56068        */
 56069  	rc = ReadInt32(pPager->pjfd,pNRec,iHdrOfft);
 56070  	if( rc != UNQLITE_OK ){
 56071  		return rc;
 56072  	}
 56073  	iHdrOfft += 4;
 56074  	rc = ReadInt32(pPager->pjfd,&pPager->cksumInit,iHdrOfft);
 56075  	if( rc != UNQLITE_OK ){
 56076  		return rc;
 56077  	}
 56078  	iHdrOfft += 4;
 56079  	rc = ReadInt64(pPager->pjfd,pDbSize,iHdrOfft);
 56080  	if( rc != UNQLITE_OK ){
 56081  		return rc;
 56082  	}
 56083  	iHdrOfft += 8;
 56084  	/* Read the page-size and sector-size journal header fields. */
 56085  	rc = ReadInt32(pPager->pjfd,&iSectorSize,iHdrOfft);
 56086  	if( rc != UNQLITE_OK ){
 56087  		return rc;
 56088  	}
 56089  	iHdrOfft += 4;
 56090  	rc = ReadInt32(pPager->pjfd,&iPageSize,iHdrOfft);
 56091  	if( rc != UNQLITE_OK ){
 56092  		return rc;
 56093  	}
 56094  	/* Check that the values read from the page-size and sector-size fields
 56095      ** are within range. To be 'in range', both values need to be a power
 56096      ** of two greater than or equal to 512 or 32, and not greater than their 
 56097      ** respective compile time maximum limits.
 56098      */
 56099      if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32
 56100       || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE
 56101       || ((iPageSize-1)&iPageSize)!=0    || ((iSectorSize-1)&iSectorSize)!=0 
 56102      ){
 56103        /* If the either the page-size or sector-size in the journal-header is 
 56104        ** invalid, then the process that wrote the journal-header must have 
 56105        ** crashed before the header was synced. In this case stop reading 
 56106        ** the journal file here.
 56107        */
 56108        return UNQLITE_DONE;
 56109      }
 56110      /* Update the assumed sector-size to match the value used by 
 56111      ** the process that created this journal. If this journal was
 56112      ** created by a process other than this one, then this routine
 56113      ** is being called from within pager_playback(). The local value
 56114      ** of Pager.sectorSize is restored at the end of that routine.
 56115      */
 56116      pPager->iSectorSize = iSectorSize;
 56117  	pPager->iPageSize = iPageSize;
 56118  	/* Ready to rollback */
 56119  	pPager->iJournalOfft = JOURNAL_HDR_SZ(pPager);
 56120  	/* All done */
 56121  	return UNQLITE_OK;
 56122  }
 56123  /*
 56124   * Write the journal header in the given memory buffer.
 56125   * The given buffer is big enough to hold the whole header.
 56126   */
 56127  static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf)
 56128  {
 56129  	unsigned char *zPtr = zBuf;
 56130  	/* 8 bytes magic number */
 56131  	SyMemcpy(aJournalMagic,zPtr,sizeof(aJournalMagic));
 56132  	zPtr += sizeof(aJournalMagic);
 56133  	/* 4 bytes: Number of records in journal. */
 56134  	SyBigEndianPack32(zPtr,0);
 56135  	zPtr += 4;
 56136  	/* 4 bytes: Random number used to compute page checksum. */
 56137  	SyBigEndianPack32(zPtr,pPager->cksumInit);
 56138  	zPtr += 4;
 56139  	/* 8 bytes: Initial database page count. */
 56140  	SyBigEndianPack64(zPtr,pPager->dbOrigSize);
 56141  	zPtr += 8;
 56142  	/* 4 bytes: Sector size used by the process that wrote this journal. */
 56143  	SyBigEndianPack32(zPtr,(sxu32)pPager->iSectorSize);
 56144  	zPtr += 4;
 56145  	/* 4 bytes: Database page size. */
 56146  	SyBigEndianPack32(zPtr,(sxu32)pPager->iPageSize);
 56147  	return UNQLITE_OK;
 56148  }
 56149  /*
 56150  ** Parameter aData must point to a buffer of pPager->pageSize bytes
 56151  ** of data. Compute and return a checksum based ont the contents of the 
 56152  ** page of data and the current value of pPager->cksumInit.
 56153  **
 56154  ** This is not a real checksum. It is really just the sum of the 
 56155  ** random initial value (pPager->cksumInit) and every 200th byte
 56156  ** of the page data, starting with byte offset (pPager->pageSize%200).
 56157  ** Each byte is interpreted as an 8-bit unsigned integer.
 56158  **
 56159  ** Changing the formula used to compute this checksum results in an
 56160  ** incompatible journal file format.
 56161  **
 56162  ** If journal corruption occurs due to a power failure, the most likely 
 56163  ** scenario is that one end or the other of the record will be changed. 
 56164  ** It is much less likely that the two ends of the journal record will be
 56165  ** correct and the middle be corrupt.  Thus, this "checksum" scheme,
 56166  ** though fast and simple, catches the mostly likely kind of corruption.
 56167  */
 56168  static sxu32 pager_cksum(Pager *pPager,const unsigned char *zData)
 56169  {
 56170    sxu32 cksum = pPager->cksumInit;         /* Checksum value to return */
 56171    int i = pPager->iPageSize-200;          /* Loop counter */
 56172    while( i>0 ){
 56173      cksum += zData[i];
 56174      i -= 200;
 56175    }
 56176    return cksum;
 56177  }
 56178  /*
 56179  ** Read a single page from the journal file opened on file descriptor
 56180  ** jfd. Playback this one page. Update the offset to read from.
 56181  */
 56182  static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zTmp)
 56183  {
 56184  	unsigned char *zData = zTmp;
 56185  	sxi64 iOfft; /* Offset to read from */
 56186  	pgno iNum;   /* Pager number */
 56187  	sxu32 ckSum; /* Sanity check */
 56188  	int rc;
 56189  	/* Offset to start reading from */
 56190  	iOfft = *pOfft;
 56191  	/* Database page number */
 56192  	rc = ReadInt64(pPager->pjfd,&iNum,iOfft);
 56193  	if( rc != UNQLITE_OK ){ return rc; }
 56194  	iOfft += 8;
 56195  	/* Page data */
 56196  	rc = unqliteOsRead(pPager->pjfd,zData,pPager->iPageSize,iOfft);
 56197  	if( rc != UNQLITE_OK ){ return rc; }
 56198  	iOfft += pPager->iPageSize;
 56199  	/* Page cksum */
 56200  	rc = ReadInt32(pPager->pjfd,&ckSum,iOfft);
 56201  	if( rc != UNQLITE_OK ){ return rc; }
 56202  	iOfft += 4;
 56203  	/* Synchronize pointers */
 56204  	*pOfft = iOfft;
 56205  	/* Make sure we are dealing with a valid page */
 56206  	if( ckSum != pager_cksum(pPager,zData) ){
 56207  		/* Ignore that page */
 56208  		return SXERR_IGNORE;
 56209  	}
 56210  	if( iNum >= pPager->dbSize ){
 56211  		/* Ignore that page */
 56212  		return UNQLITE_OK;
 56213  	}
 56214  	/* playback */
 56215  	rc = unqliteOsWrite(pPager->pfd,zData,pPager->iPageSize,iNum * pPager->iPageSize);
 56216  	if( rc == UNQLITE_OK ){
 56217  		/* Flush the cache */
 56218  		pager_fill_page(pPager,iNum,zData);
 56219  	}
 56220  	return rc;
 56221  }
 56222  /*
 56223  ** Playback the journal and thus restore the database file to
 56224  ** the state it was in before we started making changes.  
 56225  **
 56226  ** The journal file format is as follows: 
 56227  **
 56228  **  (1)  8 byte prefix.  A copy of aJournalMagic[].
 56229  **  (2)  4 byte big-endian integer which is the number of valid page records
 56230  **       in the journal. 
 56231  **  (3)  4 byte big-endian integer which is the initial value for the 
 56232  **       sanity checksum.
 56233  **  (4)  8 byte integer which is the number of pages to truncate the
 56234  **       database to during a rollback.
 56235  **  (5)  4 byte big-endian integer which is the sector size.  The header
 56236  **       is this many bytes in size.
 56237  **  (6)  4 byte big-endian integer which is the page size.
 56238  **  (7)  zero padding out to the next sector size.
 56239  **  (8)  Zero or more pages instances, each as follows:
 56240  **        +  4 byte page number.
 56241  **        +  pPager->pageSize bytes of data.
 56242  **        +  4 byte checksum
 56243  **
 56244  ** When we speak of the journal header, we mean the first 7 items above.
 56245  ** Each entry in the journal is an instance of the 8th item.
 56246  **
 56247  ** Call the value from the second bullet "nRec".  nRec is the number of
 56248  ** valid page entries in the journal.  In most cases, you can compute the
 56249  ** value of nRec from the size of the journal file.  But if a power
 56250  ** failure occurred while the journal was being written, it could be the
 56251  ** case that the size of the journal file had already been increased but
 56252  ** the extra entries had not yet made it safely to disk.  In such a case,
 56253  ** the value of nRec computed from the file size would be too large.  For
 56254  ** that reason, we always use the nRec value in the header.
 56255  **
 56256  ** If the file opened as the journal file is not a well-formed
 56257  ** journal file then all pages up to the first corrupted page are rolled
 56258  ** back (or no pages if the journal header is corrupted). The journal file
 56259  ** is then deleted and SQLITE_OK returned, just as if no corruption had
 56260  ** been encountered.
 56261  **
 56262  ** If an I/O or malloc() error occurs, the journal-file is not deleted
 56263  ** and an error code is returned.
 56264  **
 56265  */
 56266  static int pager_playback(Pager *pPager)
 56267  {
 56268  	unsigned char *zTmp = 0; /* cc warning */
 56269  	sxu32 n,nRec;
 56270  	sxi64 iOfft;
 56271  	int rc;
 56272  	/* Read the journal header*/
 56273  	rc = pager_read_journal_header(pPager,&nRec,&pPager->dbSize);
 56274  	if( rc != UNQLITE_OK ){
 56275  		if( rc == UNQLITE_DONE ){
 56276  			goto end_playback;
 56277  		}
 56278  		unqliteGenErrorFormat(pPager->pDb,"IO error while reading journal file '%s' header",pPager->zJournal);
 56279  		return rc;
 56280  	}
 56281  	/* Truncate the database back to its original size */
 56282  	rc = unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
 56283  	if( rc != UNQLITE_OK ){
 56284  		unqliteGenError(pPager->pDb,"IO error while truncating database file");
 56285  		return rc;
 56286  	}
 56287  	/* Allocate a temporary page */
 56288  	zTmp = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
 56289  	if( zTmp == 0 ){
 56290  		unqliteGenOutofMem(pPager->pDb);
 56291  		return UNQLITE_NOMEM;
 56292  	}
 56293  	SyZero((void *)zTmp,(sxu32)pPager->iPageSize);
 56294  	/* Copy original pages out of the journal and back into the 
 56295      ** database file and/or page cache.
 56296      */
 56297  	iOfft = pPager->iJournalOfft;
 56298  	for( n = 0 ; n < nRec ; ++n ){
 56299  		rc = pager_play_back_one_page(pPager,&iOfft,zTmp);
 56300  		if( rc != UNQLITE_OK ){
 56301  			if( rc != SXERR_IGNORE ){
 56302  				unqliteGenError(pPager->pDb,"Page playback error");
 56303  				goto end_playback;
 56304  			}
 56305  		}
 56306  	}
 56307  end_playback:
 56308  	/* Release the temp page */
 56309  	SyMemBackendFree(pPager->pAllocator,(void *)zTmp);
 56310  	if( rc == UNQLITE_OK ){
 56311  		/* Sync the database file */
 56312  		unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
 56313  	}
 56314  	if( rc == UNQLITE_DONE ){
 56315  		rc = UNQLITE_OK;
 56316  	}
 56317  	/* Return to the caller */
 56318  	return rc;
 56319  }
 56320  /*
 56321  ** Unlock the database file to level eLock, which must be either NO_LOCK
 56322  ** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
 56323  ** succeeds, set the Pager.iLock variable to match the (attempted) new lock.
 56324  **
 56325  ** Except, if Pager.iLock is set to NO_LOCK when this function is
 56326  ** called, do not modify it. See the comment above the #define of 
 56327  ** NO_LOCK for an explanation of this.
 56328  */
 56329  static int pager_unlock_db(Pager *pPager, int eLock)
 56330  {
 56331    int rc = UNQLITE_OK;
 56332    if( pPager->iLock != NO_LOCK ){
 56333      rc = unqliteOsUnlock(pPager->pfd,eLock);
 56334      pPager->iLock = eLock;
 56335    }
 56336    return rc;
 56337  }
 56338  /*
 56339  ** Lock the database file to level eLock, which must be either SHARED_LOCK,
 56340  ** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
 56341  ** Pager.eLock variable to the new locking state. 
 56342  **
 56343  ** Except, if Pager.eLock is set to NO_LOCK when this function is 
 56344  ** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. 
 56345  ** See the comment above the #define of NO_LOCK for an explanation 
 56346  ** of this.
 56347  */
 56348  static int pager_lock_db(Pager *pPager, int eLock){
 56349    int rc = UNQLITE_OK;
 56350    if( pPager->iLock < eLock || pPager->iLock == NO_LOCK ){
 56351      rc = unqliteOsLock(pPager->pfd, eLock);
 56352      if( rc==UNQLITE_OK ){
 56353        pPager->iLock = eLock;
 56354      }else{
 56355  		unqliteGenError(pPager->pDb,
 56356  			rc == UNQLITE_BUSY ? "Another process or thread hold the requested lock" : "Error while requesting database lock"
 56357  			);
 56358  	}
 56359    }
 56360    return rc;
 56361  }
 56362  /*
 56363  ** Try to obtain a lock of type locktype on the database file. If
 56364  ** a similar or greater lock is already held, this function is a no-op
 56365  ** (returning UNQLITE_OK immediately).
 56366  **
 56367  ** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke 
 56368  ** the busy callback if the lock is currently not available. Repeat 
 56369  ** until the busy callback returns false or until the attempt to 
 56370  ** obtain the lock succeeds.
 56371  **
 56372  ** Return UNQLITE_OK on success and an error code if we cannot obtain
 56373  ** the lock. If the lock is obtained successfully, set the Pager.state 
 56374  ** variable to locktype before returning.
 56375  */
 56376  static int pager_wait_on_lock(Pager *pPager, int locktype){
 56377    int rc;                              /* Return code */
 56378    do {
 56379      rc = pager_lock_db(pPager,locktype);
 56380    }while( rc==UNQLITE_BUSY && pPager->xBusyHandler && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
 56381    return rc;
 56382  }
 56383  /*
 56384  ** This function is called after transitioning from PAGER_OPEN to
 56385  ** PAGER_SHARED state. It tests if there is a hot journal present in
 56386  ** the file-system for the given pager. A hot journal is one that 
 56387  ** needs to be played back. According to this function, a hot-journal
 56388  ** file exists if the following criteria are met:
 56389  **
 56390  **   * The journal file exists in the file system, and
 56391  **   * No process holds a RESERVED or greater lock on the database file, and
 56392  **   * The database file itself is greater than 0 bytes in size, and
 56393  **   * The first byte of the journal file exists and is not 0x00.
 56394  **
 56395  ** If the current size of the database file is 0 but a journal file
 56396  ** exists, that is probably an old journal left over from a prior
 56397  ** database with the same name. In this case the journal file is
 56398  ** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK
 56399  ** is returned.
 56400  **
 56401  ** If a hot-journal file is found to exist, *pExists is set to 1 and 
 56402  ** UNQLITE_OK returned. If no hot-journal file is present, *pExists is
 56403  ** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying
 56404  ** to determine whether or not a hot-journal file exists, the IO error
 56405  ** code is returned and the value of *pExists is undefined.
 56406  */
 56407  static int pager_has_hot_journal(Pager *pPager, int *pExists)
 56408  {
 56409    unqlite_vfs *pVfs = pPager->pVfs;
 56410    int rc = UNQLITE_OK;           /* Return code */
 56411    int exists = 1;               /* True if a journal file is present */
 56412  
 56413    *pExists = 0;
 56414    rc = unqliteOsAccess(pVfs, pPager->zJournal, UNQLITE_ACCESS_EXISTS, &exists);
 56415    if( rc==UNQLITE_OK && exists ){
 56416      int locked = 0;             /* True if some process holds a RESERVED lock */
 56417  
 56418      /* Race condition here:  Another process might have been holding the
 56419      ** the RESERVED lock and have a journal open at the unqliteOsAccess() 
 56420      ** call above, but then delete the journal and drop the lock before
 56421      ** we get to the following unqliteOsCheckReservedLock() call.  If that
 56422      ** is the case, this routine might think there is a hot journal when
 56423      ** in fact there is none.  This results in a false-positive which will
 56424      ** be dealt with by the playback routine.
 56425      */
 56426      rc = unqliteOsCheckReservedLock(pPager->pfd, &locked);
 56427      if( rc==UNQLITE_OK && !locked ){
 56428        sxi64 n = 0;                    /* Size of db file in bytes */
 56429   
 56430        /* Check the size of the database file. If it consists of 0 pages,
 56431        ** then delete the journal file. See the header comment above for 
 56432        ** the reasoning here.  Delete the obsolete journal file under
 56433        ** a RESERVED lock to avoid race conditions.
 56434        */
 56435        rc = unqliteOsFileSize(pPager->pfd,&n);
 56436        if( rc==UNQLITE_OK ){
 56437          if( n < 1 ){
 56438            if( pager_lock_db(pPager, RESERVED_LOCK)==UNQLITE_OK ){
 56439              unqliteOsDelete(pVfs, pPager->zJournal, 0);
 56440  			pager_unlock_db(pPager, SHARED_LOCK);
 56441            }
 56442          }else{
 56443            /* The journal file exists and no other connection has a reserved
 56444            ** or greater lock on the database file. */
 56445  			*pExists = 1;
 56446          }
 56447        }
 56448      }
 56449    }
 56450    return rc;
 56451  }
 56452  /*
 56453   * Rollback a journal file. (See block-comment above).
 56454   */
 56455  static int pager_journal_rollback(Pager *pPager,int check_hot)
 56456  {
 56457  	int rc;
 56458  	if( check_hot ){
 56459  		int iExists = 0; /* cc warning */
 56460  		/* Check if the journal file exists */
 56461  		rc = pager_has_hot_journal(pPager,&iExists);
 56462  		if( rc != UNQLITE_OK  ){
 56463  			/* IO error */
 56464  			return rc;
 56465  		}
 56466  		if( !iExists ){
 56467  			/* Journal file does not exists */
 56468  			return UNQLITE_OK;
 56469  		}
 56470  	}
 56471  	if( pPager->is_rdonly ){
 56472  		unqliteGenErrorFormat(pPager->pDb,
 56473  			"Cannot rollback journal file '%s' due to a read-only database handle",pPager->zJournal);
 56474  		return UNQLITE_READ_ONLY;
 56475  	}
 56476  	/* Get an EXCLUSIVE lock on the database file. At this point it is
 56477        ** important that a RESERVED lock is not obtained on the way to the
 56478        ** EXCLUSIVE lock. If it were, another process might open the
 56479        ** database file, detect the RESERVED lock, and conclude that the
 56480        ** database is safe to read while this process is still rolling the 
 56481        ** hot-journal back.
 56482        ** 
 56483        ** Because the intermediate RESERVED lock is not requested, any
 56484        ** other process attempting to access the database file will get to 
 56485        ** this point in the code and fail to obtain its own EXCLUSIVE lock 
 56486        ** on the database file.
 56487        **
 56488        ** Unless the pager is in locking_mode=exclusive mode, the lock is
 56489        ** downgraded to SHARED_LOCK before this function returns.
 56490        */
 56491  	/* Open the journal file */
 56492  	rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,&pPager->pjfd,UNQLITE_OPEN_READWRITE);
 56493  	if( rc != UNQLITE_OK ){
 56494  		unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: '%s'",pPager->zJournal);
 56495  		goto fail;
 56496  	}
 56497  	rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
 56498  	if( rc != UNQLITE_OK ){
 56499  		unqliteGenError(pPager->pDb,"Cannot acquire an exclusive lock on the database while journal rollback");
 56500  		goto fail;
 56501  	}
 56502  	/* Sync the journal file */
 56503  	unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
 56504  	/* Finally rollback the database */
 56505  	rc = pager_playback(pPager);
 56506  	/* Switch back to shared lock */
 56507  	pager_unlock_db(pPager,SHARED_LOCK);
 56508  fail:
 56509  	/* Close the journal handle */
 56510  	unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
 56511  	pPager->pjfd = 0;
 56512  	if( rc == UNQLITE_OK ){
 56513  		/* Delete the journal file */
 56514  		unqliteOsDelete(pPager->pVfs,pPager->zJournal,TRUE);
 56515  	}
 56516  	return rc;
 56517  }
 56518  /*
 56519   * Write the unqlite header (First page). (Big-Endian)
 56520   */
 56521  static int pager_write_db_header(Pager *pPager)
 56522  {
 56523  	unsigned char *zRaw = pPager->pHeader->zData;
 56524  	unqlite_kv_engine *pEngine = pPager->pEngine;
 56525  	sxu32 nDos;
 56526  	sxu16 nLen;
 56527  	/* Database signature */
 56528  	SyMemcpy(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1);
 56529  	zRaw += sizeof(UNQLITE_DB_SIG)-1;
 56530  	/* Database magic number */
 56531  	SyBigEndianPack32(zRaw,UNQLITE_DB_MAGIC);
 56532  	zRaw += 4; /* 4 byte magic number */
 56533  	/* Database creation time */
 56534  	SyZero(&pPager->tmCreate,sizeof(Sytm));
 56535  	if( pPager->pVfs->xCurrentTime ){
 56536  		pPager->pVfs->xCurrentTime(pPager->pVfs,&pPager->tmCreate);
 56537  	}
 56538  	/* DOS time format (4 bytes) */
 56539  	SyTimeFormatToDos(&pPager->tmCreate,&nDos);
 56540  	SyBigEndianPack32(zRaw,nDos);
 56541  	zRaw += 4; /* 4 byte DOS time */
 56542  	/* Sector size */
 56543  	SyBigEndianPack32(zRaw,(sxu32)pPager->iSectorSize);
 56544  	zRaw += 4; /* 4 byte sector size */
 56545  	/* Page size */
 56546  	SyBigEndianPack32(zRaw,(sxu32)pPager->iPageSize);
 56547  	zRaw += 4; /* 4 byte page size */
 56548  	/* Key value storage engine */
 56549  	nLen = (sxu16)SyStrlen(pEngine->pIo->pMethods->zName);
 56550  	SyBigEndianPack16(zRaw,nLen); /* 2 byte storage engine name */
 56551  	zRaw += 2;
 56552  	SyMemcpy((const void *)pEngine->pIo->pMethods->zName,(void *)zRaw,nLen);
 56553  	zRaw += nLen;
 56554  	/* All rest are meta-data available to the host application */
 56555  	return UNQLITE_OK;
 56556  }
 56557  /*
 56558   * Read the unqlite header (first page). (Big-Endian)
 56559   */
 56560  static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nByte)
 56561  {
 56562  	const unsigned char *zEnd = &zRaw[nByte];
 56563  	sxu32 nDos,iMagic;
 56564  	sxu16 nLen;
 56565  	char *zKv;
 56566  	/* Database signature */
 56567  	if( SyMemcmp(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1) != 0 ){
 56568  		/* Corrupt database */
 56569  		return UNQLITE_CORRUPT;
 56570  	}
 56571  	zRaw += sizeof(UNQLITE_DB_SIG)-1;
 56572  	/* Database magic number */
 56573  	SyBigEndianUnpack32(zRaw,&iMagic);
 56574  	zRaw += 4; /* 4 byte magic number */
 56575  	if( iMagic != UNQLITE_DB_MAGIC ){
 56576  		/* Corrupt database */
 56577  		return UNQLITE_CORRUPT;
 56578  	}
 56579  	/* Database creation time */
 56580  	SyBigEndianUnpack32(zRaw,&nDos);
 56581  	zRaw += 4; /* 4 byte DOS time format */
 56582  	SyDosTimeFormat(nDos,&pPager->tmCreate);
 56583  	/* Sector size */
 56584  	SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iSectorSize);
 56585  	zRaw += 4; /* 4 byte sector size */
 56586  	/* Page size */
 56587  	SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iPageSize);
 56588  	zRaw += 4; /* 4 byte page size */
 56589  	/* Check that the values read from the page-size and sector-size fields
 56590      ** are within range. To be 'in range', both values need to be a power
 56591      ** of two greater than or equal to 512 or 32, and not greater than their 
 56592      ** respective compile time maximum limits.
 56593      */
 56594      if( pPager->iPageSize<UNQLITE_MIN_PAGE_SIZE || pPager->iSectorSize<32
 56595       || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE
 56596       || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0    || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0 
 56597      ){
 56598        return UNQLITE_CORRUPT;
 56599  	}
 56600  	/* Key value storage engine */
 56601  	SyBigEndianUnpack16(zRaw,&nLen); /* 2 byte storage engine length */
 56602  	zRaw += 2;
 56603  	if( nLen > (sxu16)(zEnd - zRaw) ){
 56604  		nLen = (sxu16)(zEnd - zRaw);
 56605  	}
 56606  	zKv = (char *)SyMemBackendDup(pPager->pAllocator,(const char *)zRaw,nLen);
 56607  	if( zKv == 0 ){
 56608  		return UNQLITE_NOMEM;
 56609  	}
 56610  	SyStringInitFromBuf(&pPager->sKv,zKv,nLen);
 56611  	return UNQLITE_OK;
 56612  }
 56613  /*
 56614   * Read the database header.
 56615   */
 56616  static int pager_read_db_header(Pager *pPager)
 56617  {
 56618  	unsigned char zRaw[UNQLITE_MIN_PAGE_SIZE]; /* Minimum page size */
 56619  	sxi64 n = 0;              /* Size of db file in bytes */
 56620  	int rc;
 56621  	/* Get the file size first */
 56622  	rc = unqliteOsFileSize(pPager->pfd,&n);
 56623  	if( rc != UNQLITE_OK ){
 56624  		return rc;
 56625  	}
 56626  	pPager->dbByteSize = n;
 56627  	if( n > 0 ){
 56628  		unqlite_kv_methods *pMethods;
 56629  		SyString *pKv;
 56630  		pgno nPage;
 56631  		if( n < UNQLITE_MIN_PAGE_SIZE ){
 56632  			/* A valid unqlite database must be at least 512 bytes long */
 56633  			unqliteGenError(pPager->pDb,"Malformed database image");
 56634  			return UNQLITE_CORRUPT;
 56635  		}
 56636  		/* Read the database header */
 56637  		rc = unqliteOsRead(pPager->pfd,zRaw,sizeof(zRaw),0);
 56638  		if( rc != UNQLITE_OK ){
 56639  			unqliteGenError(pPager->pDb,"IO error while reading database header");
 56640  			return rc;
 56641  		}
 56642  		/* Extract the header */
 56643  		rc = pager_extract_header(pPager,zRaw,sizeof(zRaw));
 56644  		if( rc != UNQLITE_OK ){
 56645  			unqliteGenError(pPager->pDb,rc == UNQLITE_NOMEM ? "Unqlite is running out of memory" : "Malformed database image");
 56646  			return rc;
 56647  		}
 56648  		/* Update pager state  */
 56649  		nPage = (pgno)(n / pPager->iPageSize);
 56650  		if( nPage==0 && n>0 ){
 56651  			nPage = 1;
 56652  		}
 56653  		pPager->dbSize = nPage;
 56654  		/* Laod the target Key/Value storage engine */
 56655  		pKv = &pPager->sKv;
 56656  		pMethods = unqliteFindKVStore(pKv->zString,pKv->nByte);
 56657  		if( pMethods == 0 ){
 56658  			unqliteGenErrorFormat(pPager->pDb,"No such Key/Value storage engine '%z'",pKv);
 56659  			return UNQLITE_NOTIMPLEMENTED;
 56660  		}
 56661  		/* Install the new KV storage engine */
 56662  		rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
 56663  		if( rc != UNQLITE_OK ){
 56664  			return rc;
 56665  		}
 56666  	}else{
 56667  		/* Set a default page and sector size */
 56668  		pPager->iSectorSize = GetSectorSize(pPager->pfd);
 56669  		pPager->iPageSize = unqliteGetPageSize();
 56670  		SyStringInitFromBuf(&pPager->sKv,pPager->pEngine->pIo->pMethods->zName,SyStrlen(pPager->pEngine->pIo->pMethods->zName));
 56671  		pPager->dbSize = 0;
 56672  	}
 56673  	/* Allocate a temporary page size */
 56674  	pPager->zTmpPage = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
 56675  	if( pPager->zTmpPage == 0 ){
 56676  		unqliteGenOutofMem(pPager->pDb);
 56677  		return UNQLITE_NOMEM;
 56678  	}
 56679  	SyZero(pPager->zTmpPage,(sxu32)pPager->iPageSize);
 56680  	return UNQLITE_OK;
 56681  }
 56682  /*
 56683   * Write the database header.
 56684   */
 56685  static int pager_create_header(Pager *pPager)
 56686  {
 56687  	Page *pHeader;
 56688  	int rc;
 56689  	/* Allocate a new page */
 56690  	pHeader = pager_alloc_page(pPager,0);
 56691  	if( pHeader == 0 ){
 56692  		return UNQLITE_NOMEM;
 56693  	}
 56694  	pPager->pHeader = pHeader;
 56695  	/* Link the page */
 56696  	pager_link_page(pPager,pHeader);
 56697  	/* Add to the dirty list */
 56698  	pager_page_to_dirty_list(pPager,pHeader);
 56699  	/* Write the database header */
 56700  	rc = pager_write_db_header(pPager);
 56701  	return rc;
 56702  }
 56703  /*
 56704  ** This function is called to obtain a shared lock on the database file.
 56705  ** It is illegal to call unqlitePagerAcquire() until after this function
 56706  ** has been successfully called. If a shared-lock is already held when
 56707  ** this function is called, it is a no-op.
 56708  **
 56709  ** The following operations are also performed by this function.
 56710  **
 56711  **   1) If the pager is currently in PAGER_OPEN state (no lock held
 56712  **      on the database file), then an attempt is made to obtain a
 56713  **      SHARED lock on the database file. Immediately after obtaining
 56714  **      the SHARED lock, the file-system is checked for a hot-journal,
 56715  **      which is played back if present. 
 56716  **
 56717  ** If everything is successful, UNQLITE_OK is returned. If an IO error 
 56718  ** occurs while locking the database, checking for a hot-journal file or 
 56719  ** rolling back a journal file, the IO error code is returned.
 56720  */
 56721  static int pager_shared_lock(Pager *pPager)
 56722  {
 56723  	int rc = UNQLITE_OK;
 56724  	if( pPager->iState == PAGER_OPEN ){
 56725  		unqlite_kv_methods *pMethods;
 56726  		/* Open the target database */
 56727  		rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zFilename,&pPager->pfd,pPager->iOpenFlags);
 56728  		if( rc != UNQLITE_OK ){
 56729  			unqliteGenErrorFormat(pPager->pDb,
 56730  				"IO error while opening the target database file: %s",pPager->zFilename
 56731  				);
 56732  			return rc;
 56733  		}
 56734  		/* Try to obtain a shared lock */
 56735  		rc = pager_wait_on_lock(pPager,SHARED_LOCK);
 56736  		if( rc == UNQLITE_OK ){
 56737  			if( pPager->iLock <= SHARED_LOCK ){
 56738  				/* Rollback any hot journal */
 56739  				rc = pager_journal_rollback(pPager,1);
 56740  				if( rc != UNQLITE_OK ){
 56741  					return rc;
 56742  				}
 56743  			}
 56744  			/* Read the database header */
 56745  			rc = pager_read_db_header(pPager);
 56746  			if( rc != UNQLITE_OK ){
 56747  				return rc;
 56748  			}
 56749  			if(pPager->dbSize > 0 ){
 56750  				if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
 56751  					const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
 56752  					/* Obtain a read-only memory view of the whole file */
 56753  					if( pVfs && pVfs->xMmap ){
 56754  						int vr;
 56755  						vr = pVfs->xMmap(pPager->zFilename,&pPager->pMmap,&pPager->dbByteSize);
 56756  						if( vr != JX9_OK ){
 56757  							/* Generate a warning */
 56758  							unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
 56759  							pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
 56760  						}
 56761  					}else{
 56762  						/* Generate a warning */
 56763  						unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
 56764  						pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
 56765  					}
 56766  				}
 56767  			}
 56768  			/* Update the pager state */
 56769  			pPager->iState = PAGER_READER;
 56770  			/* Invoke the xOpen methods if available */
 56771  			pMethods = pPager->pEngine->pIo->pMethods;
 56772  			if( pMethods->xOpen ){
 56773  				rc = pMethods->xOpen(pPager->pEngine,pPager->dbSize);
 56774  				if( rc != UNQLITE_OK ){
 56775  					unqliteGenErrorFormat(pPager->pDb,
 56776  						"xOpen() method of the underlying KV engine '%z' failed",
 56777  						&pPager->sKv
 56778  						);
 56779  					pager_unlock_db(pPager,NO_LOCK);
 56780  					pPager->iState = PAGER_OPEN;
 56781  					return rc;
 56782  				}
 56783  			}
 56784  		}else if( rc == UNQLITE_BUSY ){
 56785  			unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database");
 56786  		}		
 56787  	}
 56788  	return rc;
 56789  }
 56790  /*
 56791  ** Begin a write-transaction on the specified pager object. If a 
 56792  ** write-transaction has already been opened, this function is a no-op.
 56793  */
 56794  UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager)
 56795  {
 56796  	int rc;
 56797  	/* Obtain a shared lock on the database first */
 56798  	rc = pager_shared_lock(pPager);
 56799  	if( rc != UNQLITE_OK ){
 56800  		return rc;
 56801  	}
 56802  	if( pPager->iState >= PAGER_WRITER_LOCKED ){
 56803  		return UNQLITE_OK;
 56804  	}
 56805  	if( pPager->is_rdonly ){
 56806  		unqliteGenError(pPager->pDb,"Read-only database");
 56807  		/* Read only database */
 56808  		return UNQLITE_READ_ONLY;
 56809  	}
 56810  	/* Obtain a reserved lock on the database */
 56811  	rc = pager_wait_on_lock(pPager,RESERVED_LOCK);
 56812  	if( rc == UNQLITE_OK ){
 56813  		/* Create the bitvec */
 56814  		pPager->pVec = unqliteBitvecCreate(pPager->pAllocator,pPager->dbSize);
 56815  		if( pPager->pVec == 0 ){
 56816  			unqliteGenOutofMem(pPager->pDb);
 56817  			rc = UNQLITE_NOMEM;
 56818  			goto fail;
 56819  		}
 56820  		/* Change to the WRITER_LOCK state */
 56821  		pPager->iState = PAGER_WRITER_LOCKED;
 56822  		pPager->dbOrigSize = pPager->dbSize;
 56823  		pPager->iJournalOfft = 0;
 56824  		pPager->nRec = 0;
 56825  		if( pPager->dbSize < 1 ){
 56826  			/* Write the  database header */
 56827  			rc = pager_create_header(pPager);
 56828  			if( rc != UNQLITE_OK ){
 56829  				goto fail;
 56830  			}
 56831  			pPager->dbSize = 1;
 56832  		}
 56833  	}else if( rc == UNQLITE_BUSY ){
 56834  		unqliteGenError(pPager->pDb,"Another process or thread have a reserved lock on this database");
 56835  	}
 56836  	return rc;
 56837  fail:
 56838  	/* Downgrade to shared lock */
 56839  	pager_unlock_db(pPager,SHARED_LOCK);
 56840  	return rc;
 56841  }
 56842  /*
 56843  ** This function is called at the start of every write transaction.
 56844  ** There must already be a RESERVED or EXCLUSIVE lock on the database 
 56845  ** file when this routine is called.
 56846  **
 56847  */
 56848  static int unqliteOpenJournal(Pager *pPager)
 56849  {
 56850  	unsigned char *zHeader;
 56851  	int rc = UNQLITE_OK;
 56852  	if( pPager->is_mem || pPager->no_jrnl ){
 56853  		/* Journaling is omitted for this database */
 56854  		goto finish;
 56855  	}
 56856  	if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
 56857  		/* Already opened */
 56858  		return UNQLITE_OK;
 56859  	}
 56860  	/* Delete any previously journal with the same name */
 56861  	unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
 56862  	/* Open the journal file */
 56863  	rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,
 56864  		&pPager->pjfd,UNQLITE_OPEN_CREATE|UNQLITE_OPEN_READWRITE);
 56865  	if( rc != UNQLITE_OK ){
 56866  		unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: %s",pPager->zJournal);
 56867  		return rc;
 56868  	}
 56869  	/* Write the journal header */
 56870  	zHeader = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iSectorSize);
 56871  	if( zHeader == 0 ){
 56872  		rc = UNQLITE_NOMEM;
 56873  		goto fail;
 56874  	}
 56875  	pager_write_journal_header(pPager,zHeader);
 56876  	/* Perform the disk write */
 56877  	rc = unqliteOsWrite(pPager->pjfd,zHeader,pPager->iSectorSize,0);
 56878  	/* Offset to start writing from */
 56879  	pPager->iJournalOfft = pPager->iSectorSize;
 56880  	/* All done, journal will be synced later */
 56881  	SyMemBackendFree(pPager->pAllocator,zHeader);
 56882  finish:
 56883  	if( rc == UNQLITE_OK ){
 56884  		pPager->iState = PAGER_WRITER_CACHEMOD;
 56885  		return UNQLITE_OK;
 56886  	}
 56887  fail:
 56888  	/* Unlink the journal file if something goes wrong */
 56889  	unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
 56890  	unqliteOsDelete(pPager->pVfs,pPager->zJournal,0);
 56891  	pPager->pjfd = 0;
 56892  	return rc;
 56893  }
 56894  /*
 56895  ** Sync the journal. In other words, make sure all the pages that have
 56896  ** been written to the journal have actually reached the surface of the
 56897  ** disk and can be restored in the event of a hot-journal rollback.
 56898  *
 56899  * This routine try also to obtain an exlusive lock on the database.
 56900  */
 56901  static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl)
 56902  {
 56903  	int rc;
 56904  	*pRetry = 0;
 56905  	/* Grab the exclusive lock first */
 56906  	rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
 56907  	if( rc != UNQLITE_OK ){
 56908  		/* Retry the excusive lock process */
 56909  		*pRetry = 1;
 56910  		rc = UNQLITE_OK;
 56911  	}
 56912  	if( pPager->no_jrnl ){
 56913  		/* Journaling is omitted, return immediately */
 56914  		return UNQLITE_OK;
 56915  	}
 56916  	/* Write the total number of database records */
 56917  	rc = WriteInt32(pPager->pjfd,pPager->nRec,8 /* sizeof(aJournalRec) */);
 56918  	if( rc != UNQLITE_OK ){
 56919  		if( pPager->nRec > 0 ){
 56920  			return rc;
 56921  		}else{
 56922  			/* Not so fatal */
 56923  			rc = UNQLITE_OK;
 56924  		}
 56925  	}
 56926  	/* Sync the journal and close it */
 56927  	rc = unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
 56928  	if( close_jrnl ){
 56929  		/* close the journal file */
 56930  		if( UNQLITE_OK != unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd) ){
 56931  			if( rc != UNQLITE_OK /* unqliteOsSync */ ){
 56932  				return rc;
 56933  			}
 56934  		}
 56935  		pPager->pjfd = 0;
 56936  	}
 56937  	if( (*pRetry) == 1 ){
 56938  		if( pager_lock_db(pPager,EXCLUSIVE_LOCK) == UNQLITE_OK ){
 56939  			/* Got exclusive lock */
 56940  			*pRetry = 0;
 56941  		}
 56942  	}
 56943  	return UNQLITE_OK;
 56944  }
 56945  /*
 56946   * Mark a single data page as writeable. The page is written into the 
 56947   * main journal as required.
 56948   */
 56949  static int page_write(Pager *pPager,Page *pPage)
 56950  {
 56951  	int rc;
 56952  	if( !pPager->is_mem && !pPager->no_jrnl ){
 56953  		/* Write the page to the transaction journal */
 56954  		if( pPage->pgno < pPager->dbOrigSize && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
 56955  			sxu32 cksum;
 56956  			if( pPager->nRec == SXU32_HIGH ){
 56957  				/* Journal Limit reached */
 56958  				unqliteGenError(pPager->pDb,"Journal record limit reached, commit your changes");
 56959  				return UNQLITE_LIMIT;
 56960  			}
 56961  			/* Write the page number */
 56962  			rc = WriteInt64(pPager->pjfd,pPage->pgno,pPager->iJournalOfft);
 56963  			if( rc != UNQLITE_OK ){ return rc; }
 56964  			/* Write the raw page */
 56965  			/** CODEC */
 56966  			rc = unqliteOsWrite(pPager->pjfd,pPage->zData,pPager->iPageSize,pPager->iJournalOfft + 8);
 56967  			if( rc != UNQLITE_OK ){ return rc; }
 56968  			/* Compute the checksum */
 56969  			cksum = pager_cksum(pPager,pPage->zData);
 56970  			rc = WriteInt32(pPager->pjfd,cksum,pPager->iJournalOfft + 8 + pPager->iPageSize);
 56971  			if( rc != UNQLITE_OK ){ return rc; }
 56972  			/* Update the journal offset */
 56973  			pPager->iJournalOfft += 8 /* page num */ + pPager->iPageSize + 4 /* cksum */;
 56974  			pPager->nRec++;
 56975  			/* Mark as journalled  */
 56976  			unqliteBitvecSet(pPager->pVec,pPage->pgno);
 56977  		}
 56978  	}
 56979  	/* Add the page to the dirty list */
 56980  	pager_page_to_dirty_list(pPager,pPage);
 56981  	/* Update the database size and return. */
 56982  	if( (1 + pPage->pgno) > pPager->dbSize ){
 56983  		pPager->dbSize = 1 + pPage->pgno;
 56984  		if( pPager->dbSize == SXU64_HIGH ){
 56985  			unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached");
 56986  			return UNQLITE_LIMIT;
 56987  		}
 56988  	}	
 56989  	return UNQLITE_OK;
 56990  }
 56991  /*
 56992  ** The argument is the first in a linked list of dirty pages connected
 56993  ** by the PgHdr.pDirty pointer. This function writes each one of the
 56994  ** in-memory pages in the list to the database file. The argument may
 56995  ** be NULL, representing an empty list. In this case this function is
 56996  ** a no-op.
 56997  **
 56998  ** The pager must hold at least a RESERVED lock when this function
 56999  ** is called. Before writing anything to the database file, this lock
 57000  ** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
 57001  ** UNQLITE_BUSY is returned and no data is written to the database file.
 57002  */
 57003  static int pager_write_dirty_pages(Pager *pPager,Page *pDirty)
 57004  {
 57005  	int rc = UNQLITE_OK;
 57006  	Page *pNext;
 57007  	for(;;){
 57008  		if( pDirty == 0 ){
 57009  			break;
 57010  		}
 57011  		/* Point to the next dirty page */
 57012  		pNext = pDirty->pDirtyPrev; /* Not a bug: Reverse link */
 57013  		if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
 57014  			rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
 57015  			if( rc != UNQLITE_OK ){
 57016  				/* A rollback should be done */
 57017  				break;
 57018  			}
 57019  		}
 57020  		/* Remove stale flags */
 57021  		pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
 57022  		if( pDirty->nRef < 1 ){
 57023  			/* Unlink the page now it is unused */
 57024  			pager_unlink_page(pPager,pDirty);
 57025  			/* Release the page */
 57026  			pager_release_page(pPager,pDirty);
 57027  		}
 57028  		/* Point to the next page */
 57029  		pDirty = pNext;
 57030  	}
 57031  	pPager->pDirty = pPager->pFirstDirty = 0;
 57032  	pPager->pHotDirty = pPager->pFirstHot = 0;
 57033  	pPager->nHot = 0;
 57034  	return rc;
 57035  }
 57036  /*
 57037  ** The argument is the first in a linked list of hot dirty pages connected
 57038  ** by the PgHdr.pHotDirty pointer. This function writes each one of the
 57039  ** in-memory pages in the list to the database file. The argument may
 57040  ** be NULL, representing an empty list. In this case this function is
 57041  ** a no-op.
 57042  **
 57043  ** The pager must hold at least a RESERVED lock when this function
 57044  ** is called. Before writing anything to the database file, this lock
 57045  ** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
 57046  ** UNQLITE_BUSY is returned and no data is written to the database file.
 57047  */
 57048  static int pager_write_hot_dirty_pages(Pager *pPager,Page *pDirty)
 57049  {
 57050  	int rc = UNQLITE_OK;
 57051  	Page *pNext;
 57052  	for(;;){
 57053  		if( pDirty == 0 ){
 57054  			break;
 57055  		}
 57056  		/* Point to the next page */
 57057  		pNext = pDirty->pPrevHot; /* Not a bug: Reverse link */
 57058  		if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
 57059  			rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
 57060  			if( rc != UNQLITE_OK ){
 57061  				break;
 57062  			}
 57063  		}
 57064  		/* Remove stale flags */
 57065  		pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
 57066  		/* Unlink from the list of dirty pages */
 57067  		if( pDirty->pDirtyPrev ){
 57068  			pDirty->pDirtyPrev->pDirtyNext = pDirty->pDirtyNext;
 57069  		}else{
 57070  			pPager->pDirty = pDirty->pDirtyNext;
 57071  		}
 57072  		if( pDirty->pDirtyNext ){
 57073  			pDirty->pDirtyNext->pDirtyPrev = pDirty->pDirtyPrev;
 57074  		}else{
 57075  			pPager->pFirstDirty = pDirty->pDirtyPrev;
 57076  		}
 57077  		/* Discard */
 57078  		pager_unlink_page(pPager,pDirty);
 57079  		/* Release the page */
 57080  		pager_release_page(pPager,pDirty);
 57081  		/* Next hot page */
 57082  		pDirty = pNext;
 57083  	}
 57084  	return rc;
 57085  }
 57086  /*
 57087   * Commit a transaction: Phase one.
 57088   */
 57089  static int pager_commit_phase1(Pager *pPager)
 57090  {
 57091  	int get_excl = 0;
 57092  	Page *pDirty;
 57093  	int rc;
 57094  	/* If no database changes have been made, return early. */
 57095  	if( pPager->iState < PAGER_WRITER_CACHEMOD ){
 57096  		return UNQLITE_OK;
 57097  	}
 57098  	if( pPager->is_mem ){
 57099  		/* An in-memory database */
 57100  		return UNQLITE_OK;
 57101  	}
 57102  	if( pPager->is_rdonly ){
 57103  		/* Read-Only DB */
 57104  		unqliteGenError(pPager->pDb,"Read-Only database");
 57105  		return UNQLITE_READ_ONLY;
 57106  	}
 57107  	/* Finalize the journal file */
 57108  	rc = unqliteFinalizeJournal(pPager,&get_excl,1);
 57109  	if( rc != UNQLITE_OK ){
 57110  		return rc;
 57111  	}
 57112  	/* Get the dirty pages */
 57113  	pDirty = pager_get_dirty_pages(pPager);
 57114  	if( get_excl ){
 57115  		/* Wait one last time for the exclusive lock */
 57116  		rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
 57117  		if( rc != UNQLITE_OK ){
 57118  			unqliteGenError(pPager->pDb,"Cannot obtain an Exclusive lock on the target database");
 57119  			return rc;
 57120  		}
 57121  	}
 57122  	if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){
 57123  		/* Synce the database first if a dirty commit have been applied */
 57124  		unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL);
 57125  	}
 57126  	/* Write the dirty pages */
 57127  	rc = pager_write_dirty_pages(pPager,pDirty);
 57128  	if( rc != UNQLITE_OK ){
 57129  		/* Rollback your DB */
 57130  		pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
 57131  		pPager->pFirstDirty = pDirty;
 57132  		unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database");
 57133  		return rc;
 57134  	}
 57135  	/* If the file on disk is not the same size as the database image,
 57136       * then use unqliteOsTruncate to grow or shrink the file here.
 57137       */
 57138  	if( pPager->dbSize != pPager->dbOrigSize ){
 57139  		unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
 57140  	}
 57141  	/* Sync the database file */
 57142  	unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
 57143  	/* Remove stale flags */
 57144  	pPager->iJournalOfft = 0;
 57145  	pPager->nRec = 0;
 57146  	return UNQLITE_OK;
 57147  }
 57148  /*
 57149   * Commit a transaction: Phase two.
 57150   */
 57151  static int pager_commit_phase2(Pager *pPager)
 57152  {
 57153  	if( !pPager->is_mem ){
 57154  		if( pPager->iState == PAGER_OPEN ){
 57155  			return UNQLITE_OK;
 57156  		}
 57157  		if( pPager->iState != PAGER_READER ){
 57158  			if( !pPager->no_jrnl ){
 57159  				/* Finally, unlink the journal file */
 57160  				unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
 57161  			}
 57162  			/* Downgrade to shraed lock */
 57163  			pager_unlock_db(pPager,SHARED_LOCK);
 57164  			pPager->iState = PAGER_READER;
 57165  			if( pPager->pVec ){
 57166  				unqliteBitvecDestroy(pPager->pVec);
 57167  				pPager->pVec = 0;
 57168  			}
 57169  		}
 57170  	}
 57171  	return UNQLITE_OK;
 57172  }
 57173  /*
 57174   * Perform a dirty commit.
 57175   */
 57176  static int pager_dirty_commit(Pager *pPager)
 57177  {
 57178  	int get_excl = 0;
 57179  	Page *pHot;
 57180  	int rc;
 57181  	/* Finalize the journal file without closing it */
 57182  	rc = unqliteFinalizeJournal(pPager,&get_excl,0);
 57183  	if( rc != UNQLITE_OK ){
 57184  		/* It's not a fatal error if something goes wrong here since
 57185  		 * its not the final commit.
 57186  		 */
 57187  		return UNQLITE_OK;
 57188  	}
 57189  	/* Point to the list of hot pages */
 57190  	pHot = pager_get_hot_pages(pPager);
 57191  	if( pHot == 0 ){
 57192  		return UNQLITE_OK;
 57193  	}
 57194  	if( get_excl ){
 57195  		/* Wait one last time for the exclusive lock */
 57196  		rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
 57197  		if( rc != UNQLITE_OK ){
 57198  			/* Not so fatal, will try another time */
 57199  			return UNQLITE_OK;
 57200  		}
 57201  	}
 57202  	/* Tell that a dirty commit happen */
 57203  	pPager->iFlags |= PAGER_CTRL_DIRTY_COMMIT;
 57204  	/* Write the hot pages now */
 57205  	rc = pager_write_hot_dirty_pages(pPager,pHot);
 57206  	if( rc != UNQLITE_OK ){
 57207  		pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
 57208  		unqliteGenError(pPager->pDb,"IO error while writing hot dirty pages, rollback your database");
 57209  		return rc;
 57210  	}
 57211  	pPager->pFirstHot = pPager->pHotDirty = 0;
 57212  	pPager->nHot = 0;
 57213  	/* No need to sync the database file here, since the journal is already
 57214  	 * open here and this is not the final commit.
 57215  	 */
 57216  	return UNQLITE_OK;
 57217  }
 57218  /*
 57219  ** Commit a transaction and sync the database file for the pager pPager.
 57220  **
 57221  ** This routine ensures that:
 57222  **
 57223  **   * the journal is synced,
 57224  **   * all dirty pages are written to the database file, 
 57225  **   * the database file is truncated (if required), and
 57226  **   * the database file synced.
 57227  **   * the journal file is deleted.
 57228  */
 57229  UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager)
 57230  {
 57231  	int rc;
 57232  	/* Commit: Phase One */
 57233  	rc = pager_commit_phase1(pPager);
 57234  	if( rc != UNQLITE_OK ){
 57235  		goto fail;
 57236  	}
 57237  	/* Commit: Phase Two */
 57238  	rc = pager_commit_phase2(pPager);
 57239  	if( rc != UNQLITE_OK ){
 57240  		goto fail;
 57241  	}
 57242  	/* Remove stale flags */
 57243  	pPager->iFlags &= ~PAGER_CTRL_COMMIT_ERR;
 57244  	/* All done */
 57245  	return UNQLITE_OK;
 57246  fail:
 57247  	/* Disable the auto-commit flag */
 57248  	pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
 57249  	return rc;
 57250  }
 57251  /*
 57252   * Reset the pager to its initial state. This is caused by
 57253   * a rollback operation.
 57254   */
 57255  static int pager_reset_state(Pager *pPager,int bResetKvEngine)
 57256  {
 57257  	unqlite_kv_engine *pEngine = pPager->pEngine;
 57258  	Page *pNext,*pPtr = pPager->pAll;
 57259  	const unqlite_kv_io *pIo;
 57260  	int rc;
 57261  	/* Remove stale flags */
 57262  	pPager->iFlags &= ~(PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT);
 57263  	pPager->iJournalOfft = 0;
 57264  	pPager->nRec = 0;
 57265  	/* Database original size */
 57266  	pPager->dbSize = pPager->dbOrigSize;
 57267  	/* Discard all in-memory pages */
 57268  	for(;;){
 57269  		if( pPtr == 0 ){
 57270  			break;
 57271  		}
 57272  		pNext = pPtr->pNext; /* Reverse link */
 57273  		/* Remove stale flags */
 57274  		pPtr->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
 57275  		/* Release the page */
 57276  		pager_release_page(pPager,pPtr);
 57277  		/* Point to the next page */
 57278  		pPtr = pNext;
 57279  	}
 57280  	pPager->pAll = 0;
 57281  	pPager->nPage = 0;
 57282  	pPager->pDirty = pPager->pFirstDirty = 0;
 57283  	pPager->pHotDirty = pPager->pFirstHot = 0;
 57284  	pPager->nHot = 0;
 57285  	if( pPager->apHash ){
 57286  		/* Zero the table */
 57287  		SyZero((void *)pPager->apHash,sizeof(Page *) * pPager->nSize);
 57288  	}
 57289  	if( pPager->pVec ){
 57290  		unqliteBitvecDestroy(pPager->pVec);
 57291  		pPager->pVec = 0;
 57292  	}
 57293  	/* Switch back to shared lock */
 57294  	pager_unlock_db(pPager,SHARED_LOCK);
 57295  	pPager->iState = PAGER_READER;
 57296  	if( bResetKvEngine ){
 57297  		/* Reset the underlying KV engine */
 57298  		pIo = pEngine->pIo;
 57299  		if( pIo->pMethods->xRelease ){
 57300  			/* Call the release callback */
 57301  			pIo->pMethods->xRelease(pEngine);
 57302  		}
 57303  		/* Zero the structure */
 57304  		SyZero(pEngine,(sxu32)pIo->pMethods->szKv);
 57305  		/* Fill in */
 57306  		pEngine->pIo = pIo;
 57307  		if( pIo->pMethods->xInit ){
 57308  			/* Call the init method */
 57309  			rc = pIo->pMethods->xInit(pEngine,pPager->iPageSize);
 57310  			if( rc != UNQLITE_OK ){
 57311  				return rc;
 57312  			}
 57313  		}
 57314  		if( pIo->pMethods->xOpen ){
 57315  			/* Call the xOpen method */
 57316  			rc = pIo->pMethods->xOpen(pEngine,pPager->dbSize);
 57317  			if( rc != UNQLITE_OK ){
 57318  				return rc;
 57319  			}
 57320  		}
 57321  	}
 57322  	/* All done */
 57323  	return UNQLITE_OK;
 57324  }
 57325  /*
 57326  ** If a write transaction is open, then all changes made within the 
 57327  ** transaction are reverted and the current write-transaction is closed.
 57328  ** The pager falls back to PAGER_READER state if successful.
 57329  **
 57330  ** Otherwise, in rollback mode, this function performs two functions:
 57331  **
 57332  **   1) It rolls back the journal file, restoring all database file and 
 57333  **      in-memory cache pages to the state they were in when the transaction
 57334  **      was opened, and
 57335  **
 57336  **   2) It finalizes the journal file, so that it is not used for hot
 57337  **      rollback at any point in the future (i.e. deletion).
 57338  **
 57339  ** Finalization of the journal file (task 2) is only performed if the 
 57340  ** rollback is successful.
 57341  **
 57342  */
 57343  UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine)
 57344  {
 57345  	int rc = UNQLITE_OK;
 57346  	if( pPager->iState < PAGER_WRITER_LOCKED ){
 57347  		/* A write transaction must be opened */
 57348  		return UNQLITE_OK;
 57349  	}
 57350  	if( pPager->is_mem ){
 57351  		/* As of this release 1.1.6: Transactions are not supported for in-memory databases */
 57352  		return UNQLITE_OK;
 57353  	}
 57354  	if( pPager->is_rdonly ){
 57355  		/* Read-Only DB */
 57356  		unqliteGenError(pPager->pDb,"Read-Only database");
 57357  		return UNQLITE_READ_ONLY;
 57358  	}
 57359  	if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
 57360  		if( !pPager->no_jrnl ){
 57361  			/* Close any outstanding joural file */
 57362  			if( pPager->pjfd ){
 57363  				/* Sync the journal file */
 57364  				unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
 57365  			}
 57366  			unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
 57367  			pPager->pjfd = 0;
 57368  			if( pPager->iFlags & (PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT) ){
 57369  				/* Perform the rollback */
 57370  				rc = pager_journal_rollback(pPager,0);
 57371  				if( rc != UNQLITE_OK ){
 57372  					/* Set the auto-commit flag */
 57373  					pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
 57374  					return rc;
 57375  				}
 57376  			}
 57377  		}
 57378  		/* Unlink the journal file */
 57379  		unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
 57380  		/* Reset the pager state */
 57381  		rc = pager_reset_state(pPager,bResetKvEngine);
 57382  		if( rc != UNQLITE_OK ){
 57383  			/* Mostly an unlikely scenario */
 57384  			pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; /* Set the auto-commit flag */
 57385  			unqliteGenError(pPager->pDb,"Error while reseting pager to its initial state");
 57386  			return rc;
 57387  		}
 57388  	}else{
 57389  		/* Downgrade to shared lock */
 57390  		pager_unlock_db(pPager,SHARED_LOCK);
 57391  		pPager->iState = PAGER_READER;
 57392  	}
 57393  	return UNQLITE_OK;
 57394  }
 57395  /*
 57396   *  Mark a data page as non writeable.
 57397   */
 57398  static int unqlitePagerDontWrite(unqlite_page *pMyPage)
 57399  {
 57400  	Page *pPage = (Page *)pMyPage;
 57401  	if( pPage->pgno > 0 /* Page 0 is always writeable */ ){
 57402  		pPage->flags |= PAGE_DONT_WRITE;
 57403  	}
 57404  	return UNQLITE_OK;
 57405  }
 57406  /*
 57407  ** Mark a data page as writeable. This routine must be called before 
 57408  ** making changes to a page. The caller must check the return value 
 57409  ** of this function and be careful not to change any page data unless 
 57410  ** this routine returns UNQLITE_OK.
 57411  */
 57412  static int unqlitePageWrite(unqlite_page *pMyPage)
 57413  {
 57414  	Page *pPage = (Page *)pMyPage;
 57415  	Pager *pPager = pPage->pPager;
 57416  	int rc;
 57417  	/* Begin the write transaction */
 57418  	rc = unqlitePagerBegin(pPager);
 57419  	if( rc != UNQLITE_OK ){
 57420  		return rc;
 57421  	}
 57422  	if( pPager->iState == PAGER_WRITER_LOCKED ){
 57423  		/* The journal file needs to be opened. Higher level routines have already
 57424  		 ** obtained the necessary locks to begin the write-transaction, but the
 57425  		 ** rollback journal might not yet be open. Open it now if this is the case.
 57426  		 */
 57427  		rc = unqliteOpenJournal(pPager);
 57428  		if( rc != UNQLITE_OK ){
 57429  			return rc;
 57430  		}
 57431  	}
 57432  	if( pPager->nHot > 127 ){
 57433  		/* Write hot dirty pages */
 57434  		rc = pager_dirty_commit(pPager);
 57435  		if( rc != UNQLITE_OK ){
 57436  			/* A rollback must be done */
 57437  			unqliteGenError(pPager->pDb,"Please perform a rollback");
 57438  			return rc;
 57439  		}
 57440  	}
 57441  	/* Write the page to the journal file */
 57442  	rc = page_write(pPager,pPage);
 57443  	return rc;
 57444  }
 57445  /*
 57446  ** Acquire a reference to page number pgno in pager pPager (a page
 57447  ** reference has type unqlite_page*). If the requested reference is 
 57448  ** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned.
 57449  **
 57450  ** If the requested page is already in the cache, it is returned. 
 57451  ** Otherwise, a new page object is allocated and populated with data
 57452  ** read from the database file.
 57453  */
 57454  static int unqlitePagerAcquire(
 57455    Pager *pPager,      /* The pager open on the database file */
 57456    pgno pgno,          /* Page number to fetch */
 57457    unqlite_page **ppPage,    /* OUT: Acquired page */
 57458    int fetchOnly,      /* Cache lookup only */
 57459    int noContent       /* Do not bother reading content from disk if true */
 57460  )
 57461  {
 57462  	Page *pPage;
 57463  	int rc;
 57464  	/* Acquire a shared lock (if not yet done) on the database and rollback any hot-journal if present */
 57465  	rc = pager_shared_lock(pPager);
 57466  	if( rc != UNQLITE_OK ){
 57467  		return rc;
 57468  	}
 57469  	/* Fetch the page from the cache */
 57470  	pPage = pager_fetch_page(pPager,pgno);
 57471  	if( fetchOnly ){
 57472  		if( ppPage ){
 57473  			*ppPage = (unqlite_page *)pPage;
 57474  		}
 57475  		return pPage ? UNQLITE_OK : UNQLITE_NOTFOUND;
 57476  	}
 57477  	if( pPage == 0 ){
 57478  		/* Allocate a new page */
 57479  		pPage = pager_alloc_page(pPager,pgno);
 57480  		if( pPage == 0 ){
 57481  			unqliteGenOutofMem(pPager->pDb);
 57482  			return UNQLITE_NOMEM;
 57483  		}
 57484  		/* Read page contents */
 57485  		rc = pager_get_page_contents(pPager,pPage,noContent);
 57486  		if( rc != UNQLITE_OK ){
 57487  			SyMemBackendPoolFree(pPager->pAllocator,pPage);
 57488  			return rc;
 57489  		}
 57490  		/* Link the page */
 57491  		pager_link_page(pPager,pPage);
 57492  	}else{
 57493  		if( ppPage ){
 57494  			page_ref(pPage);
 57495  		}
 57496  	}
 57497  	/* All done, page is loaded in memeory */
 57498  	if( ppPage ){
 57499  		*ppPage = (unqlite_page *)pPage;
 57500  	}
 57501  	return UNQLITE_OK;
 57502  }
 57503  /*
 57504   * Return true if we are dealing with an in-memory database.
 57505   */
 57506  static int unqliteInMemory(const char *zFilename)
 57507  {
 57508  	sxu32 n;
 57509  	if( SX_EMPTY_STR(zFilename) ){
 57510  		/* NULL or the empty string means an in-memory database */
 57511  		return TRUE;
 57512  	}
 57513  	n = SyStrlen(zFilename);
 57514  	if( n == sizeof(":mem:") - 1 && 
 57515  		SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){
 57516  			return TRUE;
 57517  	}
 57518  	if( n == sizeof(":memory:") - 1 && 
 57519  		SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){
 57520  			return TRUE;
 57521  	}
 57522  	return FALSE;
 57523  }
 57524  /*
 57525   * Allocate a new KV cursor.
 57526   */
 57527  UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut)
 57528  {
 57529  	unqlite_kv_methods *pMethods;
 57530  	unqlite_kv_cursor *pCur;
 57531  	sxu32 nByte;
 57532  	/* Storage engine methods */
 57533  	pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
 57534  	if( pMethods->szCursor < 1 ){
 57535  		/* Implementation does not supprt cursors */
 57536  		unqliteGenErrorFormat(pDb,"Storage engine '%s' does not support cursors",pMethods->zName);
 57537  		return UNQLITE_NOTIMPLEMENTED;
 57538  	}
 57539  	nByte = pMethods->szCursor;
 57540  	if( nByte < sizeof(unqlite_kv_cursor) ){
 57541  		nByte += sizeof(unqlite_kv_cursor);
 57542  	}
 57543  	pCur = (unqlite_kv_cursor *)SyMemBackendPoolAlloc(&pDb->sMem,nByte);
 57544  	if( pCur == 0 ){
 57545  		unqliteGenOutofMem(pDb);
 57546  		return UNQLITE_NOMEM;
 57547  	}
 57548  	/* Zero the structure */
 57549  	SyZero(pCur,nByte);
 57550  	/* Save the cursor */
 57551  	pCur->pStore = pDb->sDB.pPager->pEngine;
 57552  	/* Invoke the initialization callback if any */
 57553  	if( pMethods->xCursorInit ){
 57554  		pMethods->xCursorInit(pCur);
 57555  	}
 57556  	/* All done */
 57557  	*ppOut = pCur;
 57558  	return UNQLITE_OK;
 57559  }
 57560  /*
 57561   * Release a cursor.
 57562   */
 57563  UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur)
 57564  {
 57565  	unqlite_kv_methods *pMethods;
 57566  	/* Storage engine methods */
 57567  	pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
 57568  	/* Invoke the release callback if available */
 57569  	if( pMethods->xCursorRelease ){
 57570  		pMethods->xCursorRelease(pCur);
 57571  	}
 57572  	/* Finally, free the whole instance */
 57573  	SyMemBackendPoolFree(&pDb->sMem,pCur);
 57574  	return UNQLITE_OK;
 57575  }
 57576  /*
 57577   * Release the underlying KV storage engine and invoke
 57578   * its associated callbacks if available.
 57579   */
 57580  static void pager_release_kv_engine(Pager *pPager)
 57581  {
 57582  	unqlite_kv_engine *pEngine = pPager->pEngine;
 57583  	unqlite_db *pStorage = &pPager->pDb->sDB;
 57584  	if( pStorage->pCursor ){
 57585  		/* Release the associated cursor */
 57586  		unqliteReleaseCursor(pPager->pDb,pStorage->pCursor);
 57587  		pStorage->pCursor = 0;
 57588  	}
 57589  	if( pEngine->pIo->pMethods->xRelease ){
 57590  		pEngine->pIo->pMethods->xRelease(pEngine);
 57591  	}
 57592  	/* Release the whole instance */
 57593  	SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine->pIo);
 57594  	SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine);
 57595  	pPager->pEngine = 0;
 57596  }
 57597  /* Forward declaration */
 57598  static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo);
 57599  /*
 57600   * Allocate, initialize and register a new KV storage engine
 57601   * within this database instance.
 57602   */
 57603  UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods)
 57604  {
 57605  	unqlite_db *pStorage = &pPager->pDb->sDB;
 57606  	unqlite *pDb = pPager->pDb;
 57607  	unqlite_kv_engine *pEngine;
 57608  	unqlite_kv_io *pIo;
 57609  	sxu32 nByte;
 57610  	int rc;
 57611  	if( pPager->pEngine ){
 57612  		if( pMethods == pPager->pEngine->pIo->pMethods ){
 57613  			/* Ticket 1432: Same implementation */
 57614  			return UNQLITE_OK;
 57615  		}
 57616  		/* Release the old KV engine */
 57617  		pager_release_kv_engine(pPager);
 57618  	}
 57619  	/* Allocate a new KV engine instance */
 57620  	nByte = (sxu32)pMethods->szKv;
 57621  	pEngine = (unqlite_kv_engine *)SyMemBackendAlloc(&pDb->sMem,nByte);
 57622  	if( pEngine == 0 ){
 57623  		unqliteGenOutofMem(pDb);
 57624  		return UNQLITE_NOMEM;
 57625  	}
 57626  	pIo = (unqlite_kv_io *)SyMemBackendAlloc(&pDb->sMem,sizeof(unqlite_kv_io));
 57627  	if( pIo == 0 ){
 57628  		SyMemBackendFree(&pDb->sMem,pEngine);
 57629  		unqliteGenOutofMem(pDb);
 57630  		return UNQLITE_NOMEM;
 57631  	}
 57632  	/* Zero the structure */
 57633  	SyZero(pIo,sizeof(unqlite_io_methods));
 57634  	SyZero(pEngine,nByte);
 57635  	/* Populate the IO structure */
 57636  	pager_kv_io_init(pPager,pMethods,pIo);
 57637  	pEngine->pIo = pIo;
 57638  	/* Invoke the init callback if avaialble */
 57639  	if( pMethods->xInit ){
 57640  		rc = pMethods->xInit(pEngine,unqliteGetPageSize());
 57641  		if( rc != UNQLITE_OK ){
 57642  			unqliteGenErrorFormat(pDb,
 57643  				"xInit() method of the underlying KV engine '%z' failed",&pPager->sKv);
 57644  			goto fail;
 57645  		}
 57646  		pEngine->pIo = pIo;
 57647  	}
 57648  	pPager->pEngine = pEngine;
 57649  	/* Allocate a new cursor */
 57650  	rc = unqliteInitCursor(pDb,&pStorage->pCursor);
 57651  	if( rc != UNQLITE_OK ){
 57652  		goto fail;
 57653  	}
 57654  	return UNQLITE_OK;
 57655  fail:
 57656  	SyMemBackendFree(&pDb->sMem,pEngine);
 57657  	SyMemBackendFree(&pDb->sMem,pIo);
 57658  	return rc;
 57659  }
 57660  /*
 57661   * Return the underlying KV storage engine instance.
 57662   */
 57663  UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb)
 57664  {
 57665  	return pDb->sDB.pPager->pEngine;
 57666  }
 57667  /*
 57668  * Allocate and initialize a new Pager object. The pager should
 57669  * eventually be freed by passing it to unqlitePagerClose().
 57670  *
 57671  * The zFilename argument is the path to the database file to open.
 57672  * If zFilename is NULL or ":memory:" then all information is held
 57673  * in cache. It is never written to disk.  This can be used to implement
 57674  * an in-memory database.
 57675  */
 57676  UNQLITE_PRIVATE int unqlitePagerOpen(
 57677    unqlite_vfs *pVfs,       /* The virtual file system to use */
 57678    unqlite *pDb,            /* Database handle */
 57679    const char *zFilename,   /* Name of the database file to open */
 57680    unsigned int iFlags      /* flags controlling this file */
 57681    )
 57682  {
 57683  	unqlite_kv_methods *pMethods = 0;
 57684  	int is_mem,rd_only,no_jrnl;
 57685  	Pager *pPager;
 57686  	sxu32 nByte;
 57687  	sxu32 nLen;
 57688  	int rc;
 57689  
 57690  	/* Select the appropriate KV storage subsytem  */
 57691  	if( (iFlags & UNQLITE_OPEN_IN_MEMORY) || unqliteInMemory(zFilename) ){
 57692  		/* An in-memory database, record that  */
 57693  		pMethods = unqliteFindKVStore("mem",sizeof("mem") - 1); /* Always available */
 57694  		iFlags |= UNQLITE_OPEN_IN_MEMORY;
 57695  	}else{
 57696  		/* Install the default key value storage subsystem [i.e. Linear Hash] */
 57697  		pMethods = unqliteFindKVStore("hash",sizeof("hash")-1);
 57698  		if( pMethods == 0 ){
 57699  			/* Use the b+tree storage backend if the linear hash storage is not available */
 57700  			pMethods = unqliteFindKVStore("btree",sizeof("btree")-1);
 57701  		}
 57702  	}
 57703  	if( pMethods == 0 ){
 57704  		/* Can't happen */
 57705  		unqliteGenError(pDb,"Cannot install a default Key/Value storage engine");
 57706  		return UNQLITE_NOTIMPLEMENTED;
 57707  	}
 57708  	is_mem = (iFlags & UNQLITE_OPEN_IN_MEMORY) != 0;
 57709  	rd_only = (iFlags & UNQLITE_OPEN_READONLY) != 0;
 57710  	no_jrnl = (iFlags & UNQLITE_OPEN_OMIT_JOURNALING) != 0;
 57711  	rc = UNQLITE_OK;
 57712  	if( is_mem ){
 57713  		/* Omit journaling for in-memory database */
 57714  		no_jrnl = 1;
 57715  	}
 57716  	/* Total number of bytes to allocate */
 57717  	nByte = sizeof(Pager);
 57718  	nLen = 0;
 57719  	if( !is_mem ){
 57720  		nLen = SyStrlen(zFilename);
 57721  		nByte += pVfs->mxPathname + nLen + sizeof(char) /* null termniator */;
 57722  	}
 57723  	/* Allocate */
 57724  	pPager = (Pager *)SyMemBackendAlloc(&pDb->sMem,nByte);
 57725  	if( pPager == 0 ){
 57726  		return UNQLITE_NOMEM;
 57727  	}
 57728  	/* Zero the structure */
 57729  	SyZero(pPager,nByte);
 57730  	/* Fill-in the structure */
 57731  	pPager->pAllocator = &pDb->sMem;
 57732  	pPager->pDb = pDb;
 57733  	pDb->sDB.pPager = pPager;
 57734  	/* Allocate page table */
 57735  	pPager->nSize = 128; /* Must be a power of two */
 57736  	nByte = pPager->nSize * sizeof(Page *);
 57737  	pPager->apHash = (Page **)SyMemBackendAlloc(pPager->pAllocator,nByte);
 57738  	if( pPager->apHash == 0 ){
 57739  		rc = UNQLITE_NOMEM;
 57740  		goto fail;
 57741  	}
 57742  	SyZero(pPager->apHash,nByte);
 57743  	pPager->is_mem = is_mem;
 57744  	pPager->no_jrnl = no_jrnl;
 57745  	pPager->is_rdonly = rd_only;
 57746  	pPager->iOpenFlags = iFlags;
 57747  	pPager->pVfs = pVfs;
 57748  	SyRandomnessInit(&pPager->sPrng,0,0);
 57749  	SyRandomness(&pPager->sPrng,(void *)&pPager->cksumInit,sizeof(sxu32));
 57750  	/* Unlimited cache size */
 57751  	pPager->nCacheMax = SXU32_HIGH;
 57752  	/* Copy filename and journal name */
 57753  	if( !is_mem ){
 57754  		pPager->zFilename = (char *)&pPager[1];
 57755  		rc = UNQLITE_OK;
 57756  		if( pVfs->xFullPathname ){
 57757  			rc = pVfs->xFullPathname(pVfs,zFilename,pVfs->mxPathname + nLen,pPager->zFilename);
 57758  		}
 57759  		if( rc != UNQLITE_OK ){
 57760  			/* Simple filename copy */
 57761  			SyMemcpy(zFilename,pPager->zFilename,nLen);
 57762  			pPager->zFilename[nLen] = 0;
 57763  			rc = UNQLITE_OK;
 57764  		}else{
 57765  			nLen = SyStrlen(pPager->zFilename);
 57766  		}
 57767  		pPager->zJournal = (char *) SyMemBackendAlloc(pPager->pAllocator,nLen + sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) + sizeof(char));
 57768  		if( pPager->zJournal == 0 ){
 57769  			rc = UNQLITE_NOMEM;
 57770  			goto fail;
 57771  		}
 57772  		/* Copy filename */
 57773  		SyMemcpy(pPager->zFilename,pPager->zJournal,nLen);
 57774  		/* Copy journal suffix */
 57775  		SyMemcpy(UNQLITE_JOURNAL_FILE_SUFFIX,&pPager->zJournal[nLen],sizeof(UNQLITE_JOURNAL_FILE_SUFFIX)-1);
 57776  		/* Append the nul terminator to the journal path */
 57777  		pPager->zJournal[nLen + ( sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) - 1)] = 0;
 57778  	}
 57779  	/* Finally, register the selected KV engine */
 57780  	rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
 57781  	if( rc != UNQLITE_OK ){
 57782  		goto fail;
 57783  	}
 57784  	/* Set the pager state */
 57785  	if( pPager->is_mem ){
 57786  		pPager->iState = PAGER_WRITER_FINISHED;
 57787  		pPager->iLock = EXCLUSIVE_LOCK;
 57788  	}else{
 57789  		pPager->iState = PAGER_OPEN;
 57790  		pPager->iLock = NO_LOCK;
 57791  	}
 57792  	/* All done, ready for processing */
 57793  	return UNQLITE_OK;
 57794  fail:
 57795  	SyMemBackendFree(&pDb->sMem,pPager);
 57796  	return rc;
 57797  }
 57798  /*
 57799   * Set a cache limit. Note that, this is a simple hint, the pager is not
 57800   * forced to honor this limit.
 57801   */
 57802  UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage)
 57803  {
 57804  	if( mxPage < 256 ){
 57805  		return UNQLITE_INVALID;
 57806  	}
 57807  	pPager->nCacheMax = mxPage;
 57808  	return UNQLITE_OK;
 57809  }
 57810  /*
 57811   * Shutdown the page cache. Free all memory and close the database file.
 57812   */
 57813  UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager)
 57814  {
 57815  	/* Release the KV engine */
 57816  	pager_release_kv_engine(pPager);
 57817  	if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
 57818  		const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
 57819  		if( pVfs && pVfs->xUnmap && pPager->pMmap ){
 57820  			pVfs->xUnmap(pPager->pMmap,pPager->dbByteSize);
 57821  		}
 57822  	}
 57823  	if( !pPager->is_mem && pPager->iState > PAGER_OPEN ){
 57824  		/* Release all lock on this database handle */
 57825  		pager_unlock_db(pPager,NO_LOCK);
 57826  		/* Close the file  */
 57827  		unqliteOsCloseFree(pPager->pAllocator,pPager->pfd);
 57828  	}
 57829  	if( pPager->pVec ){
 57830  		unqliteBitvecDestroy(pPager->pVec);
 57831  		pPager->pVec = 0;
 57832  	}
 57833  	return UNQLITE_OK;
 57834  }
 57835  /*
 57836   * Generate a random string.
 57837   */
 57838  UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen)
 57839  {
 57840  	static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
 57841  	sxu32 i;
 57842  	/* Generate a binary string first */
 57843  	SyRandomness(&pPager->sPrng,zBuf,nLen);
 57844  	/* Turn the binary string into english based alphabet */
 57845  	for( i = 0 ; i < nLen ; ++i ){
 57846  		 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
 57847  	 }
 57848  }
 57849  /*
 57850   * Generate a random number.
 57851   */
 57852  UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager)
 57853  {
 57854  	sxu32 iNum;
 57855  	SyRandomness(&pPager->sPrng,(void *)&iNum,sizeof(iNum));
 57856  	return iNum;
 57857  }
 57858  /* Exported KV IO Methods */
 57859  /* 
 57860   * Refer to [unqlitePagerAcquire()]
 57861   */
 57862  static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
 57863  {
 57864  	int rc;
 57865  	rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0);
 57866  	return rc;
 57867  }
 57868  /* 
 57869   * Refer to [unqlitePagerAcquire()]
 57870   */
 57871  static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
 57872  {
 57873  	int rc;
 57874  	rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0);
 57875  	return rc;
 57876  }
 57877  /* 
 57878   * Refer to [unqlitePagerAcquire()]
 57879   */
 57880  static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage)
 57881  {
 57882  	Pager *pPager = (Pager *)pHandle;
 57883  	int rc;
 57884  	/* 
 57885  	 * Acquire a reader-lock first so that pPager->dbSize get initialized.
 57886  	 */
 57887  	rc = pager_shared_lock(pPager);
 57888  	if( rc == UNQLITE_OK ){
 57889  		rc = unqlitePagerAcquire(pPager,pPager->dbSize == 0 ? /* Page 0 is reserved */ 1 : pPager->dbSize ,ppPage,0,0);
 57890  	}
 57891  	return rc;
 57892  }
 57893  /* 
 57894   * Refer to [unqlitePageWrite()]
 57895   */
 57896  static int unqliteKvIopageWrite(unqlite_page *pPage)
 57897  {
 57898  	int rc;
 57899  	if( pPage == 0 ){
 57900  		/* TICKET 1433-0348 */
 57901  		return UNQLITE_OK;
 57902  	}
 57903  	rc = unqlitePageWrite(pPage);
 57904  	return rc;
 57905  }
 57906  /* 
 57907   * Refer to [unqlitePagerDontWrite()]
 57908   */
 57909  static int unqliteKvIoPageDontWrite(unqlite_page *pPage)
 57910  {
 57911  	int rc;
 57912  	if( pPage == 0 ){
 57913  		/* TICKET 1433-0348 */
 57914  		return UNQLITE_OK;
 57915  	}
 57916  	rc = unqlitePagerDontWrite(pPage);
 57917  	return rc;
 57918  }
 57919  /* 
 57920   * Refer to [unqliteBitvecSet()]
 57921   */
 57922  static int unqliteKvIoPageDontJournal(unqlite_page *pRaw)
 57923  {
 57924  	Page *pPage = (Page *)pRaw;
 57925  	Pager *pPager;
 57926  	if( pPage == 0 ){
 57927  		/* TICKET 1433-0348 */
 57928  		return UNQLITE_OK;
 57929  	}
 57930  	pPager = pPage->pPager;
 57931  	if( pPager->iState >= PAGER_WRITER_LOCKED ){
 57932  		if( !pPager->no_jrnl && pPager->pVec && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
 57933  			unqliteBitvecSet(pPager->pVec,pPage->pgno);
 57934  		}
 57935  	}
 57936  	return UNQLITE_OK;
 57937  }
 57938  /* 
 57939   * Do not add a page to the hot dirty list.
 57940   */
 57941  static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw)
 57942  {
 57943  	Page *pPage = (Page *)pRaw;
 57944  	
 57945  	if( pPage == 0 ){
 57946  		/* TICKET 1433-0348 */
 57947  		return UNQLITE_OK;
 57948  	}
 57949  	pPage->flags |= PAGE_DONT_MAKE_HOT;
 57950  	return UNQLITE_OK;
 57951  }
 57952  /* 
 57953   * Refer to [page_ref()]
 57954   */
 57955  static int unqliteKvIopage_ref(unqlite_page *pPage)
 57956  {
 57957  	if( pPage ){
 57958  		page_ref((Page *)pPage);
 57959  	}
 57960  	return UNQLITE_OK;
 57961  }
 57962  /* 
 57963   * Refer to [page_unref()]
 57964   */
 57965  static int unqliteKvIoPageUnRef(unqlite_page *pPage)
 57966  {
 57967  	if( pPage ){
 57968  		page_unref((Page *)pPage);
 57969  	}
 57970  	return UNQLITE_OK;
 57971  }
 57972  /* 
 57973   * Refer to the declaration of the [Pager] structure
 57974   */
 57975  static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle)
 57976  {
 57977  	return ((Pager *)pHandle)->is_rdonly;
 57978  }
 57979  /* 
 57980   * Refer to the declaration of the [Pager] structure
 57981   */
 57982  static int unqliteKvIoPageSize(unqlite_kv_handle pHandle)
 57983  {
 57984  	return ((Pager *)pHandle)->iPageSize;
 57985  }
 57986  /* 
 57987   * Refer to the declaration of the [Pager] structure
 57988   */
 57989  static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle)
 57990  {
 57991  	return ((Pager *)pHandle)->zTmpPage;
 57992  }
 57993  /* 
 57994   * Set a page unpin callback.
 57995   * Refer to the declaration of the [Pager] structure
 57996   */
 57997  static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(void *))
 57998  {
 57999  	Pager *pPager = (Pager *)pHandle;
 58000  	pPager->xPageUnpin = xPageUnpin;
 58001  }
 58002  /* 
 58003   * Set a page reload callback.
 58004   * Refer to the declaration of the [Pager] structure
 58005   */
 58006  static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)(void *))
 58007  {
 58008  	Pager *pPager = (Pager *)pHandle;
 58009  	pPager->xPageReload = xPageReload;
 58010  }
 58011  /* 
 58012   * Log an error.
 58013   * Refer to the declaration of the [Pager] structure
 58014   */
 58015  static void unqliteKvIoErr(unqlite_kv_handle pHandle,const char *zErr)
 58016  {
 58017  	Pager *pPager = (Pager *)pHandle;
 58018  	unqliteGenError(pPager->pDb,zErr);
 58019  }
 58020  /*
 58021   * Init an instance of the [unqlite_kv_io] structure.
 58022   */
 58023  static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo)
 58024  {
 58025  	pIo->pHandle =  pPager;
 58026  	pIo->pMethods = pMethods;
 58027  	
 58028  	pIo->xGet    = unqliteKvIoPageGet;
 58029  	pIo->xLookup = unqliteKvIoPageLookup;
 58030  	pIo->xNew    = unqliteKvIoNewPage;
 58031  	
 58032  	pIo->xWrite     = unqliteKvIopageWrite; 
 58033  	pIo->xDontWrite = unqliteKvIoPageDontWrite;
 58034  	pIo->xDontJournal = unqliteKvIoPageDontJournal;
 58035  	pIo->xDontMkHot = unqliteKvIoPageDontMakeHot;
 58036  
 58037  	pIo->xPageRef   = unqliteKvIopage_ref;
 58038  	pIo->xPageUnref = unqliteKvIoPageUnRef;
 58039  
 58040  	pIo->xPageSize = unqliteKvIoPageSize;
 58041  	pIo->xReadOnly = unqliteKvIoReadOnly;
 58042  
 58043  	pIo->xTmpPage =  unqliteKvIoTempPage;
 58044  
 58045  	pIo->xSetUnpin = unqliteKvIoPageUnpin;
 58046  	pIo->xSetReload = unqliteKvIoPageReload;
 58047  
 58048  	pIo->xErr = unqliteKvIoErr;
 58049  
 58050  	return UNQLITE_OK;
 58051  }
 58052  /*
 58053   * ----------------------------------------------------------
 58054   * File: unqlite_vm.c
 58055   * MD5: 2a0c56efb2ab87d3e52d0d7c3147c53b
 58056   * ----------------------------------------------------------
 58057   */
 58058  /*
 58059   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 58060   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 58061   * Version 1.1.6
 58062   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 58063   * please contact Symisc Systems via:
 58064   *       legal@symisc.net
 58065   *       licensing@symisc.net
 58066   *       contact@symisc.net
 58067   * or visit:
 58068   *      http://unqlite.org/licensing.html
 58069   */
 58070   /* $SymiscID: unqlite_vm.c v1.0 Win7 2013-01-29 23:37 stable <chm@symisc.net> $ */
 58071  #ifndef UNQLITE_AMALGAMATION
 58072  #include "unqliteInt.h"
 58073  #endif
 58074  /* This file deals with low level stuff related to the unQLite Virtual Machine */
 58075  
 58076  /* Record ID as a hash value */
 58077  #define COL_RECORD_HASH(RID) (RID)
 58078  /*
 58079   * Fetch a record from a given collection.
 58080   */
 58081  static unqlite_col_record * CollectionCacheFetchRecord(
 58082  	unqlite_col *pCol, /* Target collection */
 58083  	jx9_int64 nId      /* Unique record ID */
 58084  	)
 58085  {
 58086  	unqlite_col_record *pEntry;
 58087  	if( pCol->nRec < 1 ){
 58088  		/* Don't bother hashing */
 58089  		return 0;
 58090  	}
 58091  	pEntry = pCol->apRecord[COL_RECORD_HASH(nId) & (pCol->nRecSize - 1)];
 58092  	for(;;){
 58093  		if( pEntry == 0 ){
 58094  			break;
 58095  		}
 58096  		if( pEntry->nId == nId ){
 58097  			/* Record found */
 58098  			return pEntry;
 58099  		}
 58100  		/* Point to the next entry */
 58101  		pEntry = pEntry->pNextCol;
 58102  
 58103  	}
 58104  	/* No such record */
 58105  	return 0;
 58106  }
 58107  /*
 58108   * Install a freshly created record in a given collection. 
 58109   */
 58110  static int CollectionCacheInstallRecord(
 58111  	unqlite_col *pCol, /* Target collection */
 58112  	jx9_int64 nId,     /* Unique record ID */
 58113  	jx9_value *pValue  /* JSON value */
 58114  	)
 58115  {
 58116  	unqlite_col_record *pRecord;
 58117  	sxu32 iBucket;
 58118  	/* Fetch the record first */
 58119  	pRecord = CollectionCacheFetchRecord(pCol,nId);
 58120  	if( pRecord ){
 58121  		/* Record already installed, overwrite its old value  */
 58122  		jx9MemObjStore(pValue,&pRecord->sValue);
 58123  		return UNQLITE_OK;
 58124  	}
 58125  	/* Allocate a new instance */
 58126  	pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record));
 58127  	if( pRecord == 0 ){
 58128  		return UNQLITE_NOMEM;
 58129  	}
 58130  	/* Zero the structure */
 58131  	SyZero(pRecord,sizeof(unqlite_col_record));
 58132  	/* Fill in the structure */
 58133  	jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue);
 58134  	jx9MemObjStore(pValue,&pRecord->sValue);
 58135  	pRecord->nId = nId;
 58136  	pRecord->pCol = pCol;
 58137  	/* Install in the corresponding bucket */
 58138  	iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
 58139  	pRecord->pNextCol = pCol->apRecord[iBucket];
 58140  	if( pCol->apRecord[iBucket] ){
 58141  		pCol->apRecord[iBucket]->pPrevCol = pRecord;
 58142  	}
 58143  	pCol->apRecord[iBucket] = pRecord;
 58144  	/* Link */
 58145  	MACRO_LD_PUSH(pCol->pList,pRecord);
 58146  	pCol->nRec++;
 58147  	if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){
 58148  		/* Allocate a new larger table */
 58149  		sxu32 nNewSize = pCol->nRecSize << 1;
 58150  		unqlite_col_record *pEntry;
 58151  		unqlite_col_record **apNew;
 58152  		sxu32 n;
 58153  		
 58154  		apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *));
 58155  		if( apNew ){
 58156  			/* Zero the new table */
 58157  			SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *));
 58158  			/* Rehash all entries */
 58159  			n = 0;
 58160  			pEntry = pCol->pList;
 58161  			for(;;){
 58162  				/* Loop one */
 58163  				if( n >= pCol->nRec ){
 58164  					break;
 58165  				}
 58166  				pEntry->pNextCol = pEntry->pPrevCol = 0;
 58167  				/* Install in the new bucket */
 58168  				iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1);
 58169  				pEntry->pNextCol = apNew[iBucket];
 58170  				if( apNew[iBucket]  ){
 58171  					apNew[iBucket]->pPrevCol = pEntry;
 58172  				}
 58173  				apNew[iBucket] = pEntry;
 58174  				/* Point to the next entry */
 58175  				pEntry = pEntry->pNext;
 58176  				n++;
 58177  			}
 58178  			/* Release the old table and reflect the change */
 58179  			SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord);
 58180  			pCol->apRecord = apNew;
 58181  			pCol->nRecSize = nNewSize;
 58182  		}
 58183  	}
 58184  	/* All done */
 58185  	return UNQLITE_OK;
 58186  }
 58187  /*
 58188   * Remove a record from the collection table.
 58189   */
 58190  UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(
 58191  	unqlite_col *pCol, /* Target collection */
 58192  	jx9_int64 nId      /* Unique record ID */
 58193  	)
 58194  {
 58195  	unqlite_col_record *pRecord;
 58196  	/* Fetch the record first */
 58197  	pRecord = CollectionCacheFetchRecord(pCol,nId);
 58198  	if( pRecord == 0 ){
 58199  		/* No such record */
 58200  		return UNQLITE_NOTFOUND;
 58201  	}
 58202  	if( pRecord->pPrevCol ){
 58203  		pRecord->pPrevCol->pNextCol = pRecord->pNextCol;
 58204  	}else{
 58205  		sxu32 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
 58206  		pCol->apRecord[iBucket] = pRecord->pNextCol;
 58207  	}
 58208  	if( pRecord->pNextCol ){
 58209  		pRecord->pNextCol->pPrevCol = pRecord->pPrevCol;
 58210  	}
 58211  	/* Unlink */
 58212  	MACRO_LD_REMOVE(pCol->pList,pRecord);
 58213  	pCol->nRec--;
 58214  	return UNQLITE_OK;
 58215  }
 58216  /*
 58217   * Discard a collection and its records.
 58218   */
 58219  static int CollectionCacheRelease(unqlite_col *pCol)
 58220  {
 58221  	unqlite_col_record *pNext,*pRec = pCol->pList;
 58222  	unqlite_vm *pVm = pCol->pVm;
 58223  	sxu32 n;
 58224  	/* Discard all records */
 58225  	for( n = 0 ; n < pCol->nRec ; ++n ){
 58226  		pNext = pRec->pNext;
 58227  		jx9MemObjRelease(&pRec->sValue);
 58228  		SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec);
 58229  		/* Point to the next record */
 58230  		pRec = pNext;
 58231  	}
 58232  	SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
 58233  	pCol->nRec = pCol->nRecSize = 0;
 58234  	pCol->pList = 0;
 58235  	return UNQLITE_OK;
 58236  }
 58237  /*
 58238   * Install a freshly created collection in the unqlite VM.
 58239   */
 58240  static int unqliteVmInstallCollection(
 58241  	unqlite_vm *pVm,  /* Target VM */
 58242  	unqlite_col *pCol /* Collection to install */
 58243  	)
 58244  {
 58245  	SyString *pName = &pCol->sName;
 58246  	sxu32 iBucket;
 58247  	/* Hash the collection name */
 58248  	pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte);
 58249  	/* Install it in the corresponding bucket */
 58250  	iBucket = pCol->nHash & (pVm->iColSize - 1);
 58251  	pCol->pNextCol = pVm->apCol[iBucket];
 58252  	if( pVm->apCol[iBucket] ){
 58253  		pVm->apCol[iBucket]->pPrevCol = pCol;
 58254  	}
 58255  	pVm->apCol[iBucket] = pCol;
 58256  	/* Link to the list of active collections */
 58257  	MACRO_LD_PUSH(pVm->pCol,pCol);
 58258  	pVm->iCol++;
 58259  	if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){
 58260  		/* Grow the hashtable */
 58261  		sxu32 nNewSize = pVm->iColSize << 1;
 58262  		unqlite_col *pEntry;
 58263  		unqlite_col **apNew;
 58264  		sxu32 n;
 58265  		
 58266  		apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *));
 58267  		if( apNew ){
 58268  			/* Zero the new table */
 58269  			SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *));
 58270  			/* Rehash all entries */
 58271  			n = 0;
 58272  			pEntry = pVm->pCol;
 58273  			for(;;){
 58274  				/* Loop one */
 58275  				if( n >= pVm->iCol ){
 58276  					break;
 58277  				}
 58278  				pEntry->pNextCol = pEntry->pPrevCol = 0;
 58279  				/* Install in the new bucket */
 58280  				iBucket = pEntry->nHash & (nNewSize - 1);
 58281  				pEntry->pNextCol = apNew[iBucket];
 58282  				if( apNew[iBucket]  ){
 58283  					apNew[iBucket]->pPrevCol = pEntry;
 58284  				}
 58285  				apNew[iBucket] = pEntry;
 58286  				/* Point to the next entry */
 58287  				pEntry = pEntry->pNext;
 58288  				n++;
 58289  			}
 58290  			/* Release the old table and reflect the change */
 58291  			SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol);
 58292  			pVm->apCol = apNew;
 58293  			pVm->iColSize  = nNewSize;
 58294  		}
 58295  	}
 58296  	return UNQLITE_OK;
 58297  }
 58298  /*
 58299   * Fetch a collection from the target VM.
 58300   */
 58301  static unqlite_col * unqliteVmFetchCollection(
 58302  	unqlite_vm *pVm, /* Target VM */
 58303  	SyString *pName  /* Lookup name */
 58304  	)
 58305  {
 58306  	unqlite_col *pCol;
 58307  	sxu32 nHash;
 58308  	if( pVm->iCol < 1 ){
 58309  		/* Don't bother hashing */
 58310  		return 0;
 58311  	}
 58312  	nHash = SyBinHash((const void *)pName->zString,pName->nByte);
 58313  	/* Perform the lookup */
 58314  	pCol = pVm->apCol[nHash & ( pVm->iColSize - 1)];
 58315  	for(;;){
 58316  		if( pCol == 0 ){
 58317  			break;
 58318  		}
 58319  		if( nHash == pCol->nHash && SyStringCmp(pName,&pCol->sName,SyMemcmp) == 0 ){
 58320  			/* Collection found */
 58321  			return pCol;
 58322  		}
 58323  		/* Point to the next entry */
 58324  		pCol = pCol->pNextCol;
 58325  	}
 58326  	/* No such collection */
 58327  	return 0;
 58328  }
 58329  /*
 58330   * Write and/or alter collection binary header.
 58331   */
 58332  static int CollectionSetHeader(
 58333  	unqlite_kv_engine *pEngine, /* Underlying KV storage engine */
 58334  	unqlite_col *pCol,          /* Target collection */
 58335  	jx9_int64 iRec,             /* Last record ID */
 58336  	jx9_int64 iTotal,           /* Total number of records in this collection */
 58337  	jx9_value *pSchema          /* Collection schema */
 58338  	)
 58339  {
 58340  	SyBlob *pHeader = &pCol->sHeader;
 58341  	unqlite_kv_methods *pMethods;
 58342  	int iWrite = 0;
 58343  	int rc;
 58344  	if( pEngine == 0 ){
 58345  		/* Default storage engine */
 58346  		pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
 58347  	}
 58348  	pMethods = pEngine->pIo->pMethods;
 58349  	if( SyBlobLength(pHeader) < 1 ){
 58350  		Sytm *pCreate = &pCol->sCreation; /* Creation time */
 58351  		unqlite_vfs *pVfs;
 58352  		sxu32 iDos;
 58353  		/* Magic number */
 58354  		rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC);
 58355  		if( rc != UNQLITE_OK ){
 58356  			return rc;
 58357  		}
 58358  		/* Initial record ID */
 58359  		rc = SyBlobAppendBig64(pHeader,0);
 58360  		if( rc != UNQLITE_OK ){
 58361  			return rc;
 58362  		}
 58363  		/* Total records in the collection */
 58364  		rc = SyBlobAppendBig64(pHeader,0);
 58365  		if( rc != UNQLITE_OK ){
 58366  			return rc;
 58367  		}
 58368  		pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs();
 58369  		/* Creation time of the collection */
 58370  		if( pVfs->xCurrentTime ){
 58371  			/* Get the creation time */
 58372  			pVfs->xCurrentTime(pVfs,pCreate);
 58373  		}else{
 58374  			/* Zero the structure */
 58375  			SyZero(pCreate,sizeof(Sytm));
 58376  		}
 58377  		/* Convert to DOS time */
 58378  		SyTimeFormatToDos(pCreate,&iDos);
 58379  		rc = SyBlobAppendBig32(pHeader,iDos);
 58380  		if( rc != UNQLITE_OK ){
 58381  			return rc;
 58382  		}
 58383  		/* Offset to start writing collection schema */
 58384  		pCol->nSchemaOfft = SyBlobLength(pHeader);
 58385  		iWrite = 1;
 58386  	}else{
 58387  		unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader);
 58388  		/* Header update */
 58389  		if( iRec >= 0 ){
 58390  			/* Update record ID */
 58391  			SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec);
 58392  			iWrite = 1;
 58393  		}
 58394  		if( iTotal >= 0 ){
 58395  			/* Total records */
 58396  			SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal);
 58397  			iWrite = 1;
 58398  		}
 58399  		if( pSchema ){
 58400  			/* Collection Schema */
 58401  			SyBlobTruncate(pHeader,pCol->nSchemaOfft);
 58402  			/* Encode the schema to FastJson */
 58403  			rc = FastJsonEncode(pSchema,pHeader,0);
 58404  			if( rc != UNQLITE_OK ){
 58405  				return rc;
 58406  			}
 58407  			/* Copy the collection schema */
 58408  			jx9MemObjStore(pSchema,&pCol->sSchema);
 58409  			iWrite = 1;
 58410  		}
 58411  	}
 58412  	if( iWrite ){
 58413  		SyString *pId = &pCol->sName;
 58414  		/* Reflect the disk and/or in-memory image */
 58415  		rc = pMethods->xReplace(pEngine,
 58416  			(const void *)pId->zString,pId->nByte,
 58417  			SyBlobData(pHeader),SyBlobLength(pHeader)
 58418  			);
 58419  		if( rc != UNQLITE_OK ){
 58420  			unqliteGenErrorFormat(pCol->pVm->pDb,
 58421  				"Cannot save collection '%z' header in the underlying storage engine",
 58422  				pId
 58423  				);
 58424  			return rc;
 58425  		}
 58426  	}
 58427  	return UNQLITE_OK;
 58428  }
 58429  /*
 58430   * Load a binary collection from disk.
 58431   */
 58432  static int CollectionLoadHeader(unqlite_col *pCol)
 58433  {
 58434  	SyBlob *pHeader = &pCol->sHeader;
 58435  	unsigned char *zRaw,*zEnd;
 58436  	sxu16 nMagic;
 58437  	sxu32 iDos;
 58438  	int rc;
 58439  	SyBlobReset(pHeader);
 58440  	/* Read the binary header */
 58441  	rc = unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pHeader);
 58442  	if( rc != UNQLITE_OK ){
 58443  		return rc;
 58444  	}
 58445  	/* Perform a sanity check */
 58446  	if( SyBlobLength(pHeader) < (2 /* magic */ + 8 /* record_id */ + 8 /* total_records */+ 4 /* DOS creation time*/) ){
 58447  		return UNQLITE_CORRUPT;
 58448  	}
 58449  	zRaw = (unsigned char *)SyBlobData(pHeader);
 58450  	zEnd = &zRaw[SyBlobLength(pHeader)];
 58451  	/* Extract the magic number */
 58452  	SyBigEndianUnpack16(zRaw,&nMagic);
 58453  	if( nMagic != UNQLITE_COLLECTION_MAGIC ){
 58454  		return UNQLITE_CORRUPT;
 58455  	}
 58456  	zRaw += 2; /* sizeof(sxu16) */
 58457  	/* Extract the record ID */
 58458  	SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nLastid);
 58459  	zRaw += 8; /* sizeof(sxu64) */
 58460  	/* Total records in the collection */
 58461  	SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nTotRec);
 58462  	/* Extract the collection creation date (DOS) */
 58463  	zRaw += 8; /* sizeof(sxu64) */
 58464  	SyBigEndianUnpack32(zRaw,&iDos);
 58465  	SyDosTimeFormat(iDos,&pCol->sCreation);
 58466  	zRaw += 4;
 58467  	/* Check for a collection schema */
 58468  	pCol->nSchemaOfft = (sxu32)(zRaw - (unsigned char *)SyBlobData(pHeader));
 58469  	if( zRaw < zEnd ){
 58470  		/* Decode the FastJson value */
 58471  		FastJsonDecode((const void *)zRaw,(sxu32)(zEnd-zRaw),&pCol->sSchema,0,0);
 58472  	}
 58473  	return UNQLITE_OK;
 58474  }
 58475  /*
 58476   * Load or create a binary collection.
 58477   */
 58478  static int unqliteVmLoadCollection(
 58479  	unqlite_vm *pVm,    /* Target VM */
 58480  	const char *zName,  /* Collection name */
 58481  	sxu32 nByte,        /* zName length */
 58482  	int iFlag,          /* Control flag */
 58483  	unqlite_col **ppOut /* OUT: in-memory collection */
 58484  	)
 58485  {
 58486  	unqlite_kv_methods *pMethods;
 58487  	unqlite_kv_engine *pEngine;
 58488  	unqlite_kv_cursor *pCursor;
 58489  	unqlite *pDb = pVm->pDb;
 58490  	unqlite_col *pCol = 0; /* cc warning */
 58491  	int rc = SXERR_MEM;
 58492  	char *zDup = 0;
 58493  	/* Point to the underlying KV store */
 58494  	pEngine = unqlitePagerGetKvEngine(pVm->pDb);
 58495  	pMethods = pEngine->pIo->pMethods;
 58496  	/* Allocate a new cursor */
 58497  	rc = unqliteInitCursor(pDb,&pCursor);
 58498  	if( rc != UNQLITE_OK ){
 58499  		return rc;
 58500  	}
 58501  	if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){
 58502  		/* Seek to the desired location */
 58503  		rc = pMethods->xSeek(pCursor,(const void *)zName,(unqlite_int64)nByte,UNQLITE_CURSOR_MATCH_EXACT);
 58504  		if( rc != UNQLITE_OK ){
 58505  			unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName);
 58506  			unqliteReleaseCursor(pDb,pCursor);
 58507  			return rc;
 58508  		}
 58509  	}
 58510  	/* Allocate a new instance */
 58511  	pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col));
 58512  	if( pCol == 0 ){
 58513  		unqliteGenOutofMem(pDb);
 58514  		rc = UNQLITE_NOMEM;
 58515  		goto fail;
 58516  	}
 58517  	SyZero(pCol,sizeof(unqlite_col));
 58518  	/* Fill in the structure */
 58519  	SyBlobInit(&pCol->sWorker,&pVm->sAlloc);
 58520  	SyBlobInit(&pCol->sHeader,&pVm->sAlloc);
 58521  	pCol->pVm = pVm;
 58522  	pCol->pCursor = pCursor;
 58523  	/* Duplicate collection name */
 58524  	zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte);
 58525  	if( zDup == 0 ){
 58526  		unqliteGenOutofMem(pDb);
 58527  		rc = UNQLITE_NOMEM;
 58528  		goto fail;
 58529  	}
 58530  	pCol->nRecSize = 64; /* Must be a power of two */
 58531  	pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *));
 58532  	if( pCol->apRecord == 0 ){
 58533  		unqliteGenOutofMem(pDb);
 58534  		rc = UNQLITE_NOMEM;
 58535  		goto fail;
 58536  	}
 58537  	/* Zero the table */
 58538  	SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *));
 58539  	SyStringInitFromBuf(&pCol->sName,zDup,nByte);
 58540  	jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema);
 58541  	if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){
 58542  		/* Create a new collection */
 58543  		if( pMethods->xReplace == 0 ){
 58544  			/* Read-only KV engine: Generate an error message and return */
 58545  			unqliteGenErrorFormat(pDb,
 58546  				"Cannot create new collection '%z' due to a read-only Key/Value storage engine",
 58547  				&pCol->sName
 58548  			);
 58549  			rc = UNQLITE_ABORT; /* Abort VM execution */
 58550  			goto fail;
 58551  		}
 58552  		/* Write the collection header */
 58553  		rc = CollectionSetHeader(pEngine,pCol,0,0,0);
 58554  		if( rc != UNQLITE_OK ){
 58555  			rc = UNQLITE_ABORT; /* Abort VM execution */
 58556  			goto fail;
 58557  		}
 58558  	}else{
 58559  		/* Read the collection header */
 58560  		rc = CollectionLoadHeader(pCol);
 58561  		if( rc != UNQLITE_OK ){
 58562  			unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName);
 58563  			goto fail;
 58564  		}
 58565  	}
 58566  	/* Finally install the collection */
 58567  	unqliteVmInstallCollection(pVm,pCol);
 58568  	/* All done */
 58569  	if( ppOut ){
 58570  		*ppOut = pCol;
 58571  	}
 58572  	return UNQLITE_OK;
 58573  fail:
 58574  	unqliteReleaseCursor(pDb,pCursor);
 58575  	if( zDup ){
 58576  		SyMemBackendFree(&pVm->sAlloc,zDup);
 58577  	}
 58578  	if( pCol ){
 58579  		if( pCol->apRecord ){
 58580  			SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
 58581  		}
 58582  		SyBlobRelease(&pCol->sHeader);
 58583  		SyBlobRelease(&pCol->sWorker);
 58584  		jx9MemObjRelease(&pCol->sSchema);
 58585  		SyMemBackendPoolFree(&pVm->sAlloc,pCol);
 58586  	}
 58587  	return rc;
 58588  }
 58589  /*
 58590   * Fetch a collection.
 58591   */
 58592  UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(
 58593  	unqlite_vm *pVm, /* Target VM */
 58594  	SyString *pName, /* Lookup key */
 58595  	int iFlag        /* Control flag */
 58596  	)
 58597  {
 58598  	unqlite_col *pCol = 0; /* cc warning */
 58599  	int rc;
 58600  	/* Check if the collection is already loaded in memory */
 58601  	pCol = unqliteVmFetchCollection(pVm,pName);
 58602  	if( pCol ){
 58603  		/* Already loaded in memory*/
 58604  		return pCol;
 58605  	}
 58606  	if( (iFlag & UNQLITE_VM_AUTO_LOAD) == 0 ){
 58607  		return 0;
 58608  	}
 58609  	/* Ask the storage engine for the collection */
 58610  	rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,0,&pCol);
 58611  	/* Return to the caller */
 58612  	return rc == UNQLITE_OK ? pCol : 0;
 58613  }
 58614  /*
 58615   * Return the unique ID of the last inserted record.
 58616   */
 58617  UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol)
 58618  {
 58619  	return pCol->nLastid == 0 ? 0 : (pCol->nLastid - 1);
 58620  }
 58621  /*
 58622   * Return the current record ID.
 58623   */
 58624  UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol)
 58625  {
 58626  	return pCol->nCurid;
 58627  }
 58628  /*
 58629   * Return the total number of records in a given collection. 
 58630   */
 58631  UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol)
 58632  {
 58633  	return pCol->nTotRec;
 58634  }
 58635  /*
 58636   * Reset the record cursor.
 58637   */
 58638  UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol)
 58639  {
 58640  	pCol->nCurid = 0;
 58641  }
 58642  /*
 58643   * Fetch a record by its unique ID.
 58644   */
 58645  UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(
 58646  	unqlite_col *pCol, /* Target collection */
 58647  	jx9_int64 nId,     /* Unique record ID */
 58648  	jx9_value *pValue  /* OUT: record value */
 58649  	)
 58650  {
 58651  	SyBlob *pWorker = &pCol->sWorker;
 58652  	unqlite_col_record *pRec;
 58653  	int rc;
 58654  	jx9_value_null(pValue);
 58655  	/* Perform a cache lookup first */
 58656  	pRec = CollectionCacheFetchRecord(pCol,nId);
 58657  	if( pRec ){
 58658  		/* Copy record value */
 58659  		jx9MemObjStore(&pRec->sValue,pValue);
 58660  		return UNQLITE_OK;
 58661  	}
 58662  	/* Reset the working buffer */
 58663  	SyBlobReset(pWorker);
 58664  	/* Generate the unique ID */
 58665  	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
 58666  	/* Reset the cursor */
 58667  	unqlite_kv_cursor_reset(pCol->pCursor);
 58668  	/* Seek the cursor to the desired location */
 58669  	rc = unqlite_kv_cursor_seek(pCol->pCursor,
 58670  		SyBlobData(pWorker),SyBlobLength(pWorker),
 58671  		UNQLITE_CURSOR_MATCH_EXACT
 58672  		);
 58673  	if( rc != UNQLITE_OK ){
 58674  		return rc;
 58675  	}
 58676  	/* Consume the binary JSON */
 58677  	SyBlobReset(pWorker);
 58678  	unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pWorker);
 58679  	if( SyBlobLength(pWorker) < 1 ){
 58680  		unqliteGenErrorFormat(pCol->pVm->pDb,
 58681  			"Empty record '%qd'",nId
 58682  			);
 58683  		jx9_value_null(pValue);
 58684  	}else{
 58685  		/* Decode the binary JSON */
 58686  		rc = FastJsonDecode(SyBlobData(pWorker),SyBlobLength(pWorker),pValue,0,0);
 58687  		if( rc == UNQLITE_OK ){
 58688  			/* Install the record in the cache */
 58689  			CollectionCacheInstallRecord(pCol,nId,pValue);
 58690  		}
 58691  	}
 58692  	return rc;
 58693  }
 58694  /*
 58695   * Fetch the next record from a given collection.
 58696   */ 
 58697  UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue)
 58698  {
 58699  	int rc;
 58700  	for(;;){
 58701  		if( pCol->nCurid >= pCol->nLastid ){
 58702  			/* No more records, reset the record cursor ID */
 58703  			pCol->nCurid = 0;
 58704  			/* Return to the caller */
 58705  			return SXERR_EOF;
 58706  		}
 58707  		rc = unqliteCollectionFetchRecordById(pCol,pCol->nCurid,pValue);
 58708  		/* Increment the record ID */
 58709  		pCol->nCurid++;
 58710  		/* Lookup result */
 58711  		if( rc == UNQLITE_OK || rc != UNQLITE_NOTFOUND ){
 58712  			break;
 58713  		}
 58714  	}
 58715  	return rc;
 58716  }
 58717  /*
 58718   * Create a new collection.
 58719   */
 58720  UNQLITE_PRIVATE int unqliteCreateCollection(
 58721  	unqlite_vm *pVm, /* Target VM */
 58722  	SyString *pName  /* Collection name */
 58723  	)
 58724  {
 58725  	unqlite_col *pCol;
 58726  	int rc;
 58727  	/* Perform a lookup first */
 58728  	pCol = unqliteCollectionFetch(pVm,pName,UNQLITE_VM_AUTO_LOAD);
 58729  	if( pCol ){
 58730  		return UNQLITE_EXISTS;
 58731  	}
 58732  	/* Now, safely create the collection */
 58733  	rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_CREATE,0);
 58734  	return rc;
 58735  }
 58736  /*
 58737   * Set a schema (JSON object) for a given collection.
 58738   */
 58739  UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue)
 58740  {
 58741  	int rc;
 58742  	if( !jx9_value_is_json_object(pValue) ){
 58743  		/* Must be a JSON object */
 58744  		return SXERR_INVALID;
 58745  	}
 58746  	rc = CollectionSetHeader(0,pCol,-1,-1,pValue);
 58747  	return rc;
 58748  }
 58749  /*
 58750   * Perform a store operation on a given collection.
 58751   */
 58752  static int CollectionStore(
 58753  	unqlite_col *pCol, /* Target collection */
 58754  	jx9_value *pValue  /* JSON value to be stored */
 58755  	)
 58756  {
 58757  	SyBlob *pWorker = &pCol->sWorker;
 58758  	unqlite_kv_methods *pMethods;
 58759  	unqlite_kv_engine *pEngine;
 58760  	sxu32 nKeyLen;
 58761  	int rc;	
 58762  	/* Point to the underlying KV store */
 58763  	pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
 58764  	pMethods = pEngine->pIo->pMethods;
 58765  	if( pCol->nTotRec >= SXI64_HIGH ){
 58766  		/* Collection limit reached. No more records */
 58767  		unqliteGenErrorFormat(pCol->pVm->pDb,
 58768  				"Collection '%z': Records limit reached",
 58769  				&pCol->sName
 58770  			);
 58771  		return UNQLITE_LIMIT;
 58772  	}
 58773  	if( pMethods->xReplace == 0 ){
 58774  		unqliteGenErrorFormat(pCol->pVm->pDb,
 58775  				"Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
 58776  				&pCol->sName
 58777  			);
 58778  		return UNQLITE_READ_ONLY;
 58779  	}
 58780  	/* Reset the working buffer */
 58781  	SyBlobReset(pWorker);
 58782  	if( jx9_value_is_json_object(pValue) ){
 58783  		jx9_value sId;
 58784  		/* If the given type is a JSON object, then add the special __id field */
 58785  		jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,pCol->nLastid);
 58786  		jx9_array_add_strkey_elem(pValue,"__id",&sId);
 58787  		jx9MemObjRelease(&sId);
 58788  	}
 58789  	/* Prepare the unique ID for this record */
 58790  	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,pCol->nLastid);
 58791  	nKeyLen = SyBlobLength(pWorker);
 58792  	if( nKeyLen < 1 ){
 58793  		unqliteGenOutofMem(pCol->pVm->pDb);
 58794  		return UNQLITE_NOMEM;
 58795  	}
 58796  	/* Turn to FastJson */
 58797  	rc = FastJsonEncode(pValue,pWorker,0);
 58798  	if( rc != UNQLITE_OK ){
 58799  		return rc;
 58800  	}
 58801  	/* Finally perform the insertion */
 58802  	rc = pMethods->xReplace(
 58803  		pEngine,
 58804  		SyBlobData(pWorker),nKeyLen,
 58805  		SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
 58806  		);
 58807  	if( rc == UNQLITE_OK ){
 58808  		/* Save the value in the cache */
 58809  		CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue);
 58810  		/* Increment the unique __id */
 58811  		pCol->nLastid++;
 58812  		pCol->nTotRec++;
 58813  		/* Reflect the change */
 58814  		rc = CollectionSetHeader(0,pCol,pCol->nLastid,pCol->nTotRec,0);
 58815  	}
 58816  	if( rc != UNQLITE_OK ){
 58817  		unqliteGenErrorFormat(pCol->pVm->pDb,
 58818  				"IO error while storing record into collection '%z'",
 58819  				&pCol->sName
 58820  			);
 58821  		return rc;
 58822  	}
 58823  	return UNQLITE_OK;
 58824  }
 58825  /*
 58826   * Array walker callback (Refer to jx9_array_walk()).
 58827   */
 58828  static int CollectionRecordArrayWalker(jx9_value *pKey,jx9_value *pData,void *pUserData)
 58829  {
 58830  	unqlite_col *pCol = (unqlite_col *)pUserData;
 58831  	int rc;
 58832  	/* Perform the insertion */
 58833  	rc = CollectionStore(pCol,pData);
 58834  	if( rc != UNQLITE_OK ){
 58835  		SXUNUSED(pKey); /* cc warning */
 58836  	}
 58837  	return rc;
 58838  }
 58839  /*
 58840   * Perform a store operation on a given collection.
 58841   */
 58842  UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag)
 58843  {
 58844  	int rc;
 58845  	if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
 58846  		/* Iterate over the array and store its members in the collection */
 58847  		rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
 58848  		SXUNUSED(iFlag); /* cc warning */
 58849  	}else{
 58850  		rc = CollectionStore(pCol,pValue);
 58851  	}
 58852  	return rc;
 58853  }
 58854  /*
 58855   * Drop a record from a given collection.
 58856   */
 58857  UNQLITE_PRIVATE int unqliteCollectionDropRecord(
 58858  	unqlite_col *pCol,  /* Target collection */
 58859  	jx9_int64 nId,      /* Unique ID of the record to be droped */
 58860  	int wr_header,      /* True to alter collection header */
 58861  	int log_err         /* True to log error */
 58862  	)
 58863  {
 58864  	SyBlob *pWorker = &pCol->sWorker;
 58865  	int rc;		
 58866  	/* Reset the working buffer */
 58867  	SyBlobReset(pWorker);
 58868  	/* Prepare the unique ID for this record */
 58869  	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
 58870  	/* Reset the cursor */
 58871  	unqlite_kv_cursor_reset(pCol->pCursor);
 58872  	/* Seek the cursor to the desired location */
 58873  	rc = unqlite_kv_cursor_seek(pCol->pCursor,
 58874  		SyBlobData(pWorker),SyBlobLength(pWorker),
 58875  		UNQLITE_CURSOR_MATCH_EXACT
 58876  		);
 58877  	if( rc != UNQLITE_OK ){
 58878  		return rc;
 58879  	}
 58880  	/* Remove the record from the storage engine */
 58881  	rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
 58882  	/* Finally, Remove the record from the cache */
 58883  	unqliteCollectionCacheRemoveRecord(pCol,nId);
 58884  	if( rc == UNQLITE_OK ){
 58885  		pCol->nTotRec--;
 58886  		if( wr_header ){
 58887  			/* Relect in the collection header */
 58888  			rc = CollectionSetHeader(0,pCol,-1,pCol->nTotRec,0);
 58889  		}
 58890  	}else if( rc == UNQLITE_NOTIMPLEMENTED ){
 58891  		if( log_err ){
 58892  			unqliteGenErrorFormat(pCol->pVm->pDb,
 58893  				"Cannot delete record from collection '%z' due to a read-only Key/Value storage engine",
 58894  				&pCol->sName
 58895  				);
 58896  		}
 58897  	}
 58898  	return rc;
 58899  }
 58900  /*
 58901   * Drop a collection from the KV storage engine and the underlying
 58902   * unqlite VM.
 58903   */
 58904  UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol)
 58905  {
 58906  	unqlite_vm *pVm = pCol->pVm;
 58907  	jx9_int64 nId;
 58908  	int rc;
 58909  	/* Reset the cursor */
 58910  	unqlite_kv_cursor_reset(pCol->pCursor);
 58911  	/* Seek the cursor to the desired location */
 58912  	rc = unqlite_kv_cursor_seek(pCol->pCursor,
 58913  		SyStringData(&pCol->sName),SyStringLength(&pCol->sName),
 58914  		UNQLITE_CURSOR_MATCH_EXACT
 58915  		);
 58916  	if( rc == UNQLITE_OK ){
 58917  		/* Remove the record from the storage engine */
 58918  		rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
 58919  	}
 58920  	if( rc != UNQLITE_OK ){
 58921  		unqliteGenErrorFormat(pCol->pVm->pDb,
 58922  				"Cannot remove collection '%z' due to a read-only Key/Value storage engine",
 58923  				&pCol->sName
 58924  			);
 58925  		return rc;
 58926  	}
 58927  	/* Drop collection records */
 58928  	for( nId = 0 ; nId < pCol->nLastid ; ++nId ){
 58929  		unqliteCollectionDropRecord(pCol,nId,0,0);
 58930  	}
 58931  	/* Cleanup */
 58932  	CollectionCacheRelease(pCol);
 58933  	SyBlobRelease(&pCol->sHeader);
 58934  	SyBlobRelease(&pCol->sWorker);
 58935  	SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName));
 58936  	unqliteReleaseCursor(pVm->pDb,pCol->pCursor);
 58937  	/* Unlink */
 58938  	if( pCol->pPrevCol ){
 58939  		pCol->pPrevCol->pNextCol = pCol->pNextCol;
 58940  	}else{
 58941  		sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1);
 58942  		pVm->apCol[iBucket] = pCol->pNextCol;
 58943  	}
 58944  	if( pCol->pNextCol ){
 58945  		pCol->pNextCol->pPrevCol = pCol->pPrevCol;
 58946  	}
 58947  	MACRO_LD_REMOVE(pVm->pCol,pCol);
 58948  	pVm->iCol--;
 58949  	SyMemBackendPoolFree(&pVm->sAlloc,pCol);
 58950  	return UNQLITE_OK;
 58951  }
 58952  /*
 58953   * ----------------------------------------------------------
 58954   * File: unqlite_jx9.c
 58955   * MD5: 8fddc15b667e85d7b5df5367132518fb
 58956   * ----------------------------------------------------------
 58957   */
 58958  /*
 58959   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 58960   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 58961   * Version 1.1.6
 58962   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 58963   * please contact Symisc Systems via:
 58964   *       legal@symisc.net
 58965   *       licensing@symisc.net
 58966   *       contact@symisc.net
 58967   * or visit:
 58968   *      http://unqlite.org/licensing.html
 58969   */
 58970   /* $SymiscID: unql_jx9.c v1.2 FreeBSD 2013-01-24 22:45 stable <chm@symisc.net> $ */
 58971  #ifndef UNQLITE_AMALGAMATION
 58972  #include "unqliteInt.h"
 58973  #endif
 58974  /* 
 58975   * This file implements UnQLite functions (db_exists(), db_create(), db_put(), db_get(), etc.) for the
 58976   * underlying Jx9 Virtual Machine. 
 58977   */
 58978  /*
 58979   * string db_version(void)
 58980   *   Return the current version of the unQLite database engine.
 58981   * Parameter
 58982   *   None
 58983   * Return
 58984   *    unQLite version number (string).
 58985   */
 58986  static int unqliteBuiltin_db_version(jx9_context *pCtx,int argc,jx9_value **argv)
 58987  {
 58988  	SXUNUSED(argc); /* cc warning */
 58989  	SXUNUSED(argv);
 58990  	jx9_result_string(pCtx,UNQLITE_VERSION,(int)sizeof(UNQLITE_VERSION)-1);
 58991  	return JX9_OK;
 58992  }
 58993  /*
 58994   * string db_errlog(void)
 58995   *   Return the database error log.
 58996   * Parameter
 58997   *   None
 58998   * Return
 58999   *    Database error log (string).
 59000   */
 59001  static int unqliteBuiltin_db_errlog(jx9_context *pCtx,int argc,jx9_value **argv)
 59002  {
 59003  	unqlite_vm *pVm;
 59004  	SyBlob *pErr;
 59005  	
 59006  	SXUNUSED(argc); /* cc warning */
 59007  	SXUNUSED(argv);
 59008  
 59009  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59010  	/* Point to the error log */
 59011  	pErr = &pVm->pDb->sErr;
 59012  	/* Return the log */
 59013  	jx9_result_string(pCtx,(const char *)SyBlobData(pErr),(int)SyBlobLength(pErr));
 59014  	return JX9_OK;
 59015  }
 59016  /*
 59017   * string db_copyright(void)
 59018   * string db_credits(void)
 59019   *   Return the unQLite database engine copyright notice.
 59020   * Parameter
 59021   *   None
 59022   * Return
 59023   *    Copyright notice.
 59024   */
 59025  static int unqliteBuiltin_db_credits(jx9_context *pCtx,int argc,jx9_value **argv)
 59026  {
 59027  	SXUNUSED(argc); /* cc warning */
 59028  	SXUNUSED(argv);
 59029  	jx9_result_string(pCtx,UNQLITE_COPYRIGHT,(int)sizeof(UNQLITE_COPYRIGHT)-1);
 59030  	return JX9_OK;
 59031  }
 59032  /*
 59033   * string db_sig(void)
 59034   *   Return the unQLite database engine unique signature.
 59035   * Parameter
 59036   *   None
 59037   * Return
 59038   *    unQLite signature.
 59039   */
 59040  static int unqliteBuiltin_db_sig(jx9_context *pCtx,int argc,jx9_value **argv)
 59041  {
 59042  	SXUNUSED(argc); /* cc warning */
 59043  	SXUNUSED(argv);
 59044  	jx9_result_string(pCtx,UNQLITE_IDENT,sizeof(UNQLITE_IDENT)-1);
 59045  	return JX9_OK;
 59046  }
 59047  /*
 59048   * bool collection_exists(string $name)
 59049   * bool db_exits(string $name)
 59050   *   Check if a given collection exists in the underlying database.
 59051   * Parameter
 59052   *   name: Lookup name
 59053   * Return
 59054   *    TRUE if the collection exits. FALSE otherwise.
 59055   */
 59056  static int unqliteBuiltin_collection_exists(jx9_context *pCtx,int argc,jx9_value **argv)
 59057  {
 59058  	unqlite_col *pCol;
 59059  	const char *zName;
 59060  	unqlite_vm *pVm;
 59061  	SyString sName;
 59062  	int nByte;
 59063  	/* Extract collection name */
 59064  	if( argc < 1 ){
 59065  		/* Missing arguments */
 59066  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59067  		/* Return false */
 59068  		jx9_result_bool(pCtx,0);
 59069  		return JX9_OK;
 59070  	}
 59071  	zName = jx9_value_to_string(argv[0],&nByte);
 59072  	if( nByte < 1){
 59073  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59074  		/* Return false */
 59075  		jx9_result_bool(pCtx,0);
 59076  		return JX9_OK;
 59077  	}
 59078  	SyStringInitFromBuf(&sName,zName,nByte);
 59079  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59080  	/* Perform the lookup */
 59081  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59082  	/* Lookup result */
 59083  	jx9_result_bool(pCtx,pCol ? 1 : 0);
 59084  	return JX9_OK;
 59085  }
 59086  /*
 59087   * bool collection_create(string $name)
 59088   * bool db_create(string $name)
 59089   *   Create a new collection.
 59090   * Parameter
 59091   *   name: Collection name
 59092   * Return
 59093   *    TRUE if the collection was successfuly created. FALSE otherwise.
 59094   */
 59095  static int unqliteBuiltin_collection_create(jx9_context *pCtx,int argc,jx9_value **argv)
 59096  {
 59097  	const char *zName;
 59098  	unqlite_vm *pVm;
 59099  	SyString sName;
 59100  	int nByte;
 59101  	int rc;
 59102  	/* Extract collection name */
 59103  	if( argc < 1 ){
 59104  		/* Missing arguments */
 59105  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59106  		/* Return false */
 59107  		jx9_result_bool(pCtx,0);
 59108  		return JX9_OK;
 59109  	}
 59110  	zName = jx9_value_to_string(argv[0],&nByte);
 59111  	if( nByte < 1){
 59112  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59113  		/* Return false */
 59114  		jx9_result_bool(pCtx,0);
 59115  		return JX9_OK;
 59116  	}
 59117  	SyStringInitFromBuf(&sName,zName,nByte);
 59118  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59119  	/* Try to create the collection */
 59120  	rc = unqliteCreateCollection(pVm,&sName);
 59121  	/* Return the result to the caller */
 59122  	jx9_result_bool(pCtx,rc == UNQLITE_OK ? 1 : 0);
 59123  	return JX9_OK;
 59124  }
 59125  /*
 59126   * value db_fetch(string $col_name)
 59127   * value db_get(string $col_name)
 59128   *   Fetch the current record from a given collection and advance
 59129   *   the record cursor.
 59130   * Parameter
 59131   *   col_name: Collection name
 59132   * Return
 59133   *    Record content success. NULL on failure (No more records to retrieve).
 59134   */
 59135  static int unqliteBuiltin_db_fetch_next(jx9_context *pCtx,int argc,jx9_value **argv)
 59136  {
 59137  	unqlite_col *pCol;
 59138  	const char *zName;
 59139  	unqlite_vm *pVm;
 59140  	SyString sName;
 59141  	int nByte;
 59142  	int rc;
 59143  	/* Extract collection name */
 59144  	if( argc < 1 ){
 59145  		/* Missing arguments */
 59146  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59147  		/* Return null */
 59148  		jx9_result_null(pCtx);
 59149  		return JX9_OK;
 59150  	}
 59151  	zName = jx9_value_to_string(argv[0],&nByte);
 59152  	if( nByte < 1){
 59153  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59154  		/* Return null */
 59155  		jx9_result_null(pCtx);
 59156  		return JX9_OK;
 59157  	}
 59158  	SyStringInitFromBuf(&sName,zName,nByte);
 59159  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59160  	/* Fetch the collection */
 59161  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59162  	if( pCol ){
 59163  		/* Fetch the current record */
 59164  		jx9_value *pValue;
 59165  		pValue = jx9_context_new_scalar(pCtx);
 59166  		if( pValue == 0 ){
 59167  			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
 59168  			jx9_result_null(pCtx);
 59169  			return JX9_OK;
 59170  		}else{
 59171  			rc = unqliteCollectionFetchNextRecord(pCol,pValue);
 59172  			if( rc == UNQLITE_OK ){
 59173  				jx9_result_value(pCtx,pValue);
 59174  				/* pValue will be automatically released as soon we return from this function */
 59175  			}else{
 59176  				/* Return null */
 59177  				jx9_result_null(pCtx);
 59178  			}
 59179  		}
 59180  	}else{
 59181  		/* No such collection, return null */
 59182  		jx9_result_null(pCtx);
 59183  	}
 59184  	return JX9_OK;
 59185  }
 59186  /*
 59187   * value db_fetch_by_id(string $col_name,int64 $record_id)
 59188   * value db_get_by_id(string $col_name,int64 $record_id)
 59189   *   Fetch a record using its unique ID from a given collection.
 59190   * Parameter
 59191   *   col_name:  Collection name
 59192   *   record_id: Record number (__id field of a JSON object)
 59193   * Return
 59194   *    Record content success. NULL on failure (No such record).
 59195   */
 59196  static int unqliteBuiltin_db_fetch_by_id(jx9_context *pCtx,int argc,jx9_value **argv)
 59197  {
 59198  	unqlite_col *pCol;
 59199  	const char *zName;
 59200  	unqlite_vm *pVm;
 59201  	SyString sName;
 59202  	jx9_int64 nId;
 59203  	int nByte;
 59204  	int rc;
 59205  	/* Extract collection name */
 59206  	if( argc < 2 ){
 59207  		/* Missing arguments */
 59208  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or record ID");
 59209  		/* Return NULL */
 59210  		jx9_result_null(pCtx);
 59211  		return JX9_OK;
 59212  	}
 59213  	zName = jx9_value_to_string(argv[0],&nByte);
 59214  	if( nByte < 1){
 59215  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59216  		/* Return NULL */
 59217  		jx9_result_null(pCtx);
 59218  		return JX9_OK;
 59219  	}
 59220  	/* Extract the record ID */
 59221  	nId = jx9_value_to_int(argv[1]);
 59222  	SyStringInitFromBuf(&sName,zName,nByte);
 59223  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59224  	/* Fetch the collection */
 59225  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59226  	if( pCol ){
 59227  		/* Fetch the desired record */
 59228  		jx9_value *pValue;
 59229  		pValue = jx9_context_new_scalar(pCtx);
 59230  		if( pValue == 0 ){
 59231  			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
 59232  			jx9_result_null(pCtx);
 59233  			return JX9_OK;
 59234  		}else{
 59235  			rc = unqliteCollectionFetchRecordById(pCol,nId,pValue);
 59236  			if( rc == UNQLITE_OK ){
 59237  				jx9_result_value(pCtx,pValue);
 59238  				/* pValue will be automatically released as soon we return from this function */
 59239  			}else{
 59240  				/* No such record, return null */
 59241  				jx9_result_null(pCtx);
 59242  			}
 59243  		}
 59244  	}else{
 59245  		/* No such collection, return null */
 59246  		jx9_result_null(pCtx);
 59247  	}
 59248  	return JX9_OK;
 59249  }
 59250  /*
 59251   * array db_fetch_all(string $col_name,[callback filter_callback])
 59252   * array db_get_all(string $col_name,[callback filter_callback])
 59253   *   Retrieve all records of a given collection and apply the given
 59254   *   callback if available to filter records.
 59255   * Parameter
 59256   *   col_name: Collection name
 59257   * Return
 59258   *    Contents of the collection (JSON array) on success. NULL on failure.
 59259   */
 59260  static int unqliteBuiltin_db_fetch_all(jx9_context *pCtx,int argc,jx9_value **argv)
 59261  {
 59262  	unqlite_col *pCol;
 59263  	const char *zName;
 59264  	unqlite_vm *pVm;
 59265  	SyString sName;
 59266  	int nByte;
 59267  	int rc;
 59268  	/* Extract collection name */
 59269  	if( argc < 1 ){
 59270  		/* Missing arguments */
 59271  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59272  		/* Return NULL */
 59273  		jx9_result_null(pCtx);
 59274  		return JX9_OK;
 59275  	}
 59276  	zName = jx9_value_to_string(argv[0],&nByte);
 59277  	if( nByte < 1){
 59278  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59279  		/* Return NULL */
 59280  		jx9_result_null(pCtx);
 59281  		return JX9_OK;
 59282  	}
 59283  	SyStringInitFromBuf(&sName,zName,nByte);
 59284  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59285  	/* Fetch the collection */
 59286  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59287  	if( pCol ){
 59288  		jx9_value *pValue,*pArray,*pCallback = 0;
 59289  		jx9_value sResult; /* Callback result */
 59290  		/* Allocate an empty scalar value and an empty JSON array */
 59291  		pArray = jx9_context_new_array(pCtx);
 59292  		pValue = jx9_context_new_scalar(pCtx);
 59293  		jx9MemObjInit(pCtx->pVm,&sResult);
 59294  		if( pValue == 0 || pArray == 0 ){
 59295  			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
 59296  			jx9_result_null(pCtx);
 59297  			return JX9_OK;
 59298  		}
 59299  		if( argc > 1 && jx9_value_is_callable(argv[1]) ){
 59300  			pCallback = argv[1];
 59301  		}
 59302  		unqliteCollectionResetRecordCursor(pCol);
 59303  		/* Fetch collection records one after one */
 59304  		while( UNQLITE_OK == unqliteCollectionFetchNextRecord(pCol,pValue) ){
 59305  			if( pCallback ){
 59306  				jx9_value *apArg[2];
 59307  				/* Invoke the filter callback */
 59308  				apArg[0] = pValue;
 59309  				rc = jx9VmCallUserFunction(pCtx->pVm,pCallback,1,apArg,&sResult);
 59310  				if( rc == JX9_OK ){
 59311  					int iResult; /* Callback result */
 59312  					/* Extract callback result */
 59313  					iResult = jx9_value_to_bool(&sResult);
 59314  					if( !iResult ){
 59315  						/* Discard the result */
 59316  						unqliteCollectionCacheRemoveRecord(pCol,unqliteCollectionCurrentRecordId(pCol) - 1);
 59317  						continue;
 59318  					}
 59319  				}
 59320  			}
 59321  			/* Put the value in the JSON array */
 59322  			jx9_array_add_elem(pArray,0,pValue);
 59323  			/* Release the value */
 59324  			jx9_value_null(pValue);
 59325  		}
 59326  		jx9MemObjRelease(&sResult);
 59327  		/* Finally, return our array */
 59328  		jx9_result_value(pCtx,pArray);
 59329  		/* pValue will be automatically released as soon we return from
 59330  		 * this foreign function.
 59331  		 */
 59332  	}else{
 59333  		/* No such collection, return null */
 59334  		jx9_result_null(pCtx);
 59335  	}
 59336  	return JX9_OK;
 59337  }
 59338  /*
 59339   * int64 db_last_record_id(string $col_name)
 59340   *   Return the ID of the last inserted record.
 59341   * Parameter
 59342   *   col_name: Collection name
 59343   * Return
 59344   *    Record ID (64-bit integer) on success. FALSE on failure.
 59345   */
 59346  static int unqliteBuiltin_db_last_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
 59347  {
 59348  	unqlite_col *pCol;
 59349  	const char *zName;
 59350  	unqlite_vm *pVm;
 59351  	SyString sName;
 59352  	int nByte;
 59353  	/* Extract collection name */
 59354  	if( argc < 1 ){
 59355  		/* Missing arguments */
 59356  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59357  		/* Return false */
 59358  		jx9_result_bool(pCtx,0);
 59359  		return JX9_OK;
 59360  	}
 59361  	zName = jx9_value_to_string(argv[0],&nByte);
 59362  	if( nByte < 1){
 59363  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59364  		/* Return false */
 59365  		jx9_result_bool(pCtx,0);
 59366  		return JX9_OK;
 59367  	}
 59368  	SyStringInitFromBuf(&sName,zName,nByte);
 59369  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59370  	/* Fetch the collection */
 59371  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59372  	if( pCol ){
 59373  		jx9_result_int64(pCtx,unqliteCollectionLastRecordId(pCol));
 59374  	}else{
 59375  		/* No such collection, return FALSE */
 59376  		jx9_result_bool(pCtx,0);
 59377  	}
 59378  	return JX9_OK;
 59379  }
 59380  /*
 59381   * inr64 db_current_record_id(string $col_name)
 59382   *   Return the current record ID.
 59383   * Parameter
 59384   *   col_name: Collection name
 59385   * Return
 59386   *    Current record ID (64-bit integer) on success. FALSE on failure.
 59387   */
 59388  static int unqliteBuiltin_db_current_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
 59389  {
 59390  	unqlite_col *pCol;
 59391  	const char *zName;
 59392  	unqlite_vm *pVm;
 59393  	SyString sName;
 59394  	int nByte;
 59395  	/* Extract collection name */
 59396  	if( argc < 1 ){
 59397  		/* Missing arguments */
 59398  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59399  		/* Return false */
 59400  		jx9_result_bool(pCtx,0);
 59401  		return JX9_OK;
 59402  	}
 59403  	zName = jx9_value_to_string(argv[0],&nByte);
 59404  	if( nByte < 1){
 59405  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59406  		/* Return false */
 59407  		jx9_result_bool(pCtx,0);
 59408  		return JX9_OK;
 59409  	}
 59410  	SyStringInitFromBuf(&sName,zName,nByte);
 59411  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59412  	/* Fetch the collection */
 59413  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59414  	if( pCol ){
 59415  		jx9_result_int64(pCtx,unqliteCollectionCurrentRecordId(pCol));
 59416  	}else{
 59417  		/* No such collection, return FALSE */
 59418  		jx9_result_bool(pCtx,0);
 59419  	}
 59420  	return JX9_OK;
 59421  }
 59422  /*
 59423   * bool db_reset_record_cursor(string $col_name)
 59424   *   Reset the record ID cursor.
 59425   * Parameter
 59426   *   col_name: Collection name
 59427   * Return
 59428   *    TRUE on success. FALSE on failure.
 59429   */
 59430  static int unqliteBuiltin_db_reset_record_cursor(jx9_context *pCtx,int argc,jx9_value **argv)
 59431  {
 59432  	unqlite_col *pCol;
 59433  	const char *zName;
 59434  	unqlite_vm *pVm;
 59435  	SyString sName;
 59436  	int nByte;
 59437  	/* Extract collection name */
 59438  	if( argc < 1 ){
 59439  		/* Missing arguments */
 59440  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59441  		/* Return false */
 59442  		jx9_result_bool(pCtx,0);
 59443  		return JX9_OK;
 59444  	}
 59445  	zName = jx9_value_to_string(argv[0],&nByte);
 59446  	if( nByte < 1){
 59447  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59448  		/* Return false */
 59449  		jx9_result_bool(pCtx,0);
 59450  		return JX9_OK;
 59451  	}
 59452  	SyStringInitFromBuf(&sName,zName,nByte);
 59453  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59454  	/* Fetch the collection */
 59455  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59456  	if( pCol ){
 59457  		unqliteCollectionResetRecordCursor(pCol);
 59458  		jx9_result_bool(pCtx,1);
 59459  	}else{
 59460  		/* No such collection */
 59461  		jx9_result_bool(pCtx,0);
 59462  	}
 59463  	return JX9_OK;
 59464  }
 59465  /*
 59466   * int64 db_total_records(string $col_name)
 59467   *   Return the total number of inserted records in the given collection.
 59468   * Parameter
 59469   *   col_name: Collection name
 59470   * Return
 59471   *    Total number of records on success. FALSE on failure.
 59472   */
 59473  static int unqliteBuiltin_db_total_records(jx9_context *pCtx,int argc,jx9_value **argv)
 59474  {
 59475  	unqlite_col *pCol;
 59476  	const char *zName;
 59477  	unqlite_vm *pVm;
 59478  	SyString sName;
 59479  	int nByte;
 59480  	/* Extract collection name */
 59481  	if( argc < 1 ){
 59482  		/* Missing arguments */
 59483  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59484  		/* Return false */
 59485  		jx9_result_bool(pCtx,0);
 59486  		return JX9_OK;
 59487  	}
 59488  	zName = jx9_value_to_string(argv[0],&nByte);
 59489  	if( nByte < 1){
 59490  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59491  		/* Return false */
 59492  		jx9_result_bool(pCtx,0);
 59493  		return JX9_OK;
 59494  	}
 59495  	SyStringInitFromBuf(&sName,zName,nByte);
 59496  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59497  	/* Fetch the collection */
 59498  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59499  	if( pCol ){
 59500  		unqlite_int64 nRec;
 59501  		nRec = unqliteCollectionTotalRecords(pCol);
 59502  		jx9_result_int64(pCtx,nRec);
 59503  	}else{
 59504  		/* No such collection */
 59505  		jx9_result_bool(pCtx,0);
 59506  	}
 59507  	return JX9_OK;
 59508  }
 59509  /*
 59510   * string db_creation_date(string $col_name)
 59511   *   Return the creation date of the given collection.
 59512   * Parameter
 59513   *   col_name: Collection name
 59514   * Return
 59515   *    Creation date on success. FALSE on failure.
 59516   */
 59517  static int unqliteBuiltin_db_creation_date(jx9_context *pCtx,int argc,jx9_value **argv)
 59518  {
 59519  	unqlite_col *pCol;
 59520  	const char *zName;
 59521  	unqlite_vm *pVm;
 59522  	SyString sName;
 59523  	int nByte;
 59524  	/* Extract collection name */
 59525  	if( argc < 1 ){
 59526  		/* Missing arguments */
 59527  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59528  		/* Return false */
 59529  		jx9_result_bool(pCtx,0);
 59530  		return JX9_OK;
 59531  	}
 59532  	zName = jx9_value_to_string(argv[0],&nByte);
 59533  	if( nByte < 1){
 59534  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59535  		/* Return false */
 59536  		jx9_result_bool(pCtx,0);
 59537  		return JX9_OK;
 59538  	}
 59539  	SyStringInitFromBuf(&sName,zName,nByte);
 59540  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59541  	/* Fetch the collection */
 59542  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59543  	if( pCol ){
 59544  		Sytm *pTm = &pCol->sCreation;
 59545  		jx9_result_string_format(pCtx,"%d-%d-%d %02d:%02d:%02d",
 59546  			pTm->tm_year,pTm->tm_mon,pTm->tm_mday,
 59547  			pTm->tm_hour,pTm->tm_min,pTm->tm_sec
 59548  			);
 59549  	}else{
 59550  		/* No such collection */
 59551  		jx9_result_bool(pCtx,0);
 59552  	}
 59553  	return JX9_OK;
 59554  }
 59555  /*
 59556   * bool db_store(string $col_name,...)
 59557   * bool db_put(string $col_name,...)
 59558   *   Store one or more JSON values in a given collection.
 59559   * Parameter
 59560   *   col_name: Collection name
 59561   * Return
 59562   *    TRUE on success. FALSE on failure.
 59563   */
 59564  static int unqliteBuiltin_db_store(jx9_context *pCtx,int argc,jx9_value **argv)
 59565  {
 59566  	unqlite_col *pCol;
 59567  	const char *zName;
 59568  	unqlite_vm *pVm;
 59569  	SyString sName;
 59570  	int nByte;
 59571  	int rc;
 59572  	int i;
 59573  	/* Extract collection name */
 59574  	if( argc < 2 ){
 59575  		/* Missing arguments */
 59576  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
 59577  		/* Return false */
 59578  		jx9_result_bool(pCtx,0);
 59579  		return JX9_OK;
 59580  	}
 59581  	zName = jx9_value_to_string(argv[0],&nByte);
 59582  	if( nByte < 1){
 59583  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59584  		/* Return false */
 59585  		jx9_result_bool(pCtx,0);
 59586  		return JX9_OK;
 59587  	}
 59588  	SyStringInitFromBuf(&sName,zName,nByte);
 59589  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59590  	/* Fetch the collection */
 59591  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59592  	if( pCol == 0 ){
 59593  		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
 59594  		/* Return false */
 59595  		jx9_result_bool(pCtx,0);
 59596  		return JX9_OK;
 59597  	}
 59598  	/* Store the given values */
 59599  	for( i = 1 ; i < argc ; ++i ){
 59600  		rc = unqliteCollectionPut(pCol,argv[i],0);
 59601  		if( rc != UNQLITE_OK){
 59602  			jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,
 59603  				"Error while storing record %d in collection '%z'",i,&sName
 59604  				);
 59605  			/* Return false */
 59606  			jx9_result_bool(pCtx,0);
 59607  			return JX9_OK;
 59608  		}
 59609  	}
 59610  	/* All done, return TRUE */
 59611  	jx9_result_bool(pCtx,1);
 59612  	return JX9_OK;
 59613  }
 59614  /*
 59615   * bool db_drop_collection(string $col_name)
 59616   * bool collection_delete(string $col_name)
 59617   *   Remove a given collection from the database.
 59618   * Parameter
 59619   *   col_name: Collection name
 59620   * Return
 59621   *    TRUE on success. FALSE on failure.
 59622   */
 59623  static int unqliteBuiltin_db_drop_col(jx9_context *pCtx,int argc,jx9_value **argv)
 59624  {
 59625  	unqlite_col *pCol;
 59626  	const char *zName;
 59627  	unqlite_vm *pVm;
 59628  	SyString sName;
 59629  	int nByte;
 59630  	int rc;
 59631  	/* Extract collection name */
 59632  	if( argc < 1 ){
 59633  		/* Missing arguments */
 59634  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
 59635  		/* Return false */
 59636  		jx9_result_bool(pCtx,0);
 59637  		return JX9_OK;
 59638  	}
 59639  	zName = jx9_value_to_string(argv[0],&nByte);
 59640  	if( nByte < 1){
 59641  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59642  		/* Return false */
 59643  		jx9_result_bool(pCtx,0);
 59644  		return JX9_OK;
 59645  	}
 59646  	SyStringInitFromBuf(&sName,zName,nByte);
 59647  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59648  	/* Fetch the collection */
 59649  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59650  	if( pCol == 0 ){
 59651  		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
 59652  		/* Return false */
 59653  		jx9_result_bool(pCtx,0);
 59654  		return JX9_OK;
 59655  	}
 59656  	/* Drop the collection */
 59657  	rc = unqliteDropCollection(pCol);
 59658  	/* Processing result */
 59659  	jx9_result_bool(pCtx,rc == UNQLITE_OK);
 59660  	return JX9_OK;
 59661  }
 59662  /*
 59663   * bool db_drop_record(string $col_name,int64 record_id)
 59664   *   Remove a given record from a collection.
 59665   * Parameter
 59666   *   col_name: Collection name.
 59667   *   record_id: ID of the record.
 59668   * Return
 59669   *    TRUE on success. FALSE on failure.
 59670   */
 59671  static int unqliteBuiltin_db_drop_record(jx9_context *pCtx,int argc,jx9_value **argv)
 59672  {
 59673  	unqlite_col *pCol;
 59674  	const char *zName;
 59675  	unqlite_vm *pVm;
 59676  	SyString sName;
 59677  	jx9_int64 nId;
 59678  	int nByte;
 59679  	int rc;
 59680  	/* Extract collection name */
 59681  	if( argc < 2 ){
 59682  		/* Missing arguments */
 59683  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
 59684  		/* Return false */
 59685  		jx9_result_bool(pCtx,0);
 59686  		return JX9_OK;
 59687  	}
 59688  	zName = jx9_value_to_string(argv[0],&nByte);
 59689  	if( nByte < 1){
 59690  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59691  		/* Return false */
 59692  		jx9_result_bool(pCtx,0);
 59693  		return JX9_OK;
 59694  	}
 59695  	SyStringInitFromBuf(&sName,zName,nByte);
 59696  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59697  	/* Fetch the collection */
 59698  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59699  	if( pCol == 0 ){
 59700  		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
 59701  		/* Return false */
 59702  		jx9_result_bool(pCtx,0);
 59703  		return JX9_OK;
 59704  	}
 59705  	/* Extract the record ID */
 59706  	nId = jx9_value_to_int64(argv[1]);
 59707  	/* Drop the record */
 59708  	rc = unqliteCollectionDropRecord(pCol,nId,1,1);
 59709  	/* Processing result */
 59710  	jx9_result_bool(pCtx,rc == UNQLITE_OK);
 59711  	return JX9_OK;
 59712  }
 59713  /*
 59714   * bool db_set_schema(string $col_name, object $json_object)
 59715   *   Set a schema for a given collection.
 59716   * Parameter
 59717   *   col_name: Collection name.
 59718   *   json_object: Collection schema (Must be a JSON object).
 59719   * Return
 59720   *    TRUE on success. FALSE on failure.
 59721   */
 59722  static int unqliteBuiltin_db_set_schema(jx9_context *pCtx,int argc,jx9_value **argv)
 59723  {
 59724  	unqlite_col *pCol;
 59725  	const char *zName;
 59726  	unqlite_vm *pVm;
 59727  	SyString sName;
 59728  	int nByte;
 59729  	int rc;
 59730  	/* Extract collection name */
 59731  	if( argc < 2 ){
 59732  		/* Missing arguments */
 59733  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
 59734  		/* Return false */
 59735  		jx9_result_bool(pCtx,0);
 59736  		return JX9_OK;
 59737  	}
 59738  	if( !jx9_value_is_json_object(argv[1]) ){
 59739  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection scheme");
 59740  		/* Return false */
 59741  		jx9_result_bool(pCtx,0);
 59742  		return JX9_OK;
 59743  	}
 59744  	zName = jx9_value_to_string(argv[0],&nByte);
 59745  	if( nByte < 1){
 59746  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59747  		/* Return false */
 59748  		jx9_result_bool(pCtx,0);
 59749  		return JX9_OK;
 59750  	}
 59751  	SyStringInitFromBuf(&sName,zName,nByte);
 59752  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59753  	/* Fetch the collection */
 59754  	rc = UNQLITE_NOOP;
 59755  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59756  	if( pCol ){
 59757  		/* Set the collection scheme */
 59758  		rc = unqliteCollectionSetSchema(pCol,argv[1]);
 59759  	}else{
 59760  		jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
 59761  			"No such collection '%z'",
 59762  			&sName
 59763  			);
 59764  	}
 59765  	/* Processing result */
 59766  	jx9_result_bool(pCtx,rc == UNQLITE_OK);
 59767  	return JX9_OK;
 59768  }
 59769  /*
 59770   * object db_get_schema(string $col_name)
 59771   *   Return the schema associated with a given collection.
 59772   * Parameter
 59773   *   col_name: Collection name
 59774   * Return
 59775   *    Collection schema on success. null otherwise.
 59776   */
 59777  static int unqliteBuiltin_db_get_schema(jx9_context *pCtx,int argc,jx9_value **argv)
 59778  {
 59779  	unqlite_col *pCol;
 59780  	const char *zName;
 59781  	unqlite_vm *pVm;
 59782  	SyString sName;
 59783  	int nByte;
 59784  	/* Extract collection name */
 59785  	if( argc < 1 ){
 59786  		/* Missing arguments */
 59787  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
 59788  		/* Return false */
 59789  		jx9_result_bool(pCtx,0);
 59790  		return JX9_OK;
 59791  	}
 59792  	zName = jx9_value_to_string(argv[0],&nByte);
 59793  	if( nByte < 1){
 59794  		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
 59795  		/* Return false */
 59796  		jx9_result_bool(pCtx,0);
 59797  		return JX9_OK;
 59798  	}
 59799  	SyStringInitFromBuf(&sName,zName,nByte);
 59800  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59801  	/* Fetch the collection */
 59802  	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
 59803  	if( pCol ){
 59804  		/* Return the collection schema */
 59805  		jx9_result_value(pCtx,&pCol->sSchema);
 59806  	}else{
 59807  		jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
 59808  			"No such collection '%z'",
 59809  			&sName
 59810  			);
 59811  		jx9_result_null(pCtx);
 59812  	}
 59813  	return JX9_OK;
 59814  }
 59815  /*
 59816   * bool db_begin(void)
 59817   *   Manually begin a write transaction.
 59818   * Parameter
 59819   *   None
 59820   * Return
 59821   *    TRUE on success. FALSE otherwise.
 59822   */
 59823  static int unqliteBuiltin_db_begin(jx9_context *pCtx,int argc,jx9_value **argv)
 59824  {
 59825  	unqlite_vm *pVm;
 59826  	unqlite *pDb;
 59827  	int rc;
 59828  	SXUNUSED(argc); /* cc warning */
 59829  	SXUNUSED(argv);
 59830  	/* Point to the unqlite Vm */
 59831  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59832  	/* Point to the underlying database handle  */
 59833  	pDb = pVm->pDb;
 59834  	/* Begin the transaction */
 59835  	rc = unqlitePagerBegin(pDb->sDB.pPager);
 59836  	/* result */
 59837  	jx9_result_bool(pCtx,rc == UNQLITE_OK );
 59838  	return JX9_OK;
 59839  }
 59840  /*
 59841   * bool db_commit(void)
 59842   *   Manually commit a transaction.
 59843   * Parameter
 59844   *   None
 59845   * Return
 59846   *    TRUE if the transaction was successfuly commited. FALSE otherwise.
 59847   */
 59848  static int unqliteBuiltin_db_commit(jx9_context *pCtx,int argc,jx9_value **argv)
 59849  {
 59850  	unqlite_vm *pVm;
 59851  	unqlite *pDb;
 59852  	int rc;
 59853  	SXUNUSED(argc); /* cc warning */
 59854  	SXUNUSED(argv);
 59855  	/* Point to the unqlite Vm */
 59856  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59857  	/* Point to the underlying database handle  */
 59858  	pDb = pVm->pDb;
 59859  	/* Commit the transaction if any */
 59860  	rc = unqlitePagerCommit(pDb->sDB.pPager);
 59861  	/* Commit result */
 59862  	jx9_result_bool(pCtx,rc == UNQLITE_OK );
 59863  	return JX9_OK;
 59864  }
 59865  /*
 59866   * bool db_rollback(void)
 59867   *   Manually rollback a transaction.
 59868   * Parameter
 59869   *   None
 59870   * Return
 59871   *    TRUE if the transaction was successfuly rolled back. FALSE otherwise
 59872   */
 59873  static int unqliteBuiltin_db_rollback(jx9_context *pCtx,int argc,jx9_value **argv)
 59874  {
 59875  	unqlite_vm *pVm;
 59876  	unqlite *pDb;
 59877  	int rc;
 59878  	SXUNUSED(argc); /* cc warning */
 59879  	SXUNUSED(argv);
 59880  	/* Point to the unqlite Vm */
 59881  	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
 59882  	/* Point to the underlying database handle  */
 59883  	pDb = pVm->pDb;
 59884  	/* Rollback the transaction if any */
 59885  	rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
 59886  	/* Rollback result */
 59887  	jx9_result_bool(pCtx,rc == UNQLITE_OK );
 59888  	return JX9_OK;
 59889  }
 59890  /*
 59891   * Register all the UnQLite foreign functions defined above.
 59892   */
 59893  UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm)
 59894  {
 59895  	static const jx9_builtin_func aBuiltin[] = {
 59896  		{ "db_version" , unqliteBuiltin_db_version },
 59897  		{ "db_copyright", unqliteBuiltin_db_credits },
 59898  		{ "db_credits" , unqliteBuiltin_db_credits },
 59899  		{ "db_sig" ,     unqliteBuiltin_db_sig     },
 59900  		{ "db_errlog",   unqliteBuiltin_db_errlog  },
 59901  		{ "collection_exists", unqliteBuiltin_collection_exists },
 59902  		{ "db_exists",         unqliteBuiltin_collection_exists }, 
 59903  		{ "collection_create", unqliteBuiltin_collection_create },
 59904  		{ "db_create",         unqliteBuiltin_collection_create },
 59905  		{ "db_fetch",          unqliteBuiltin_db_fetch_next     },
 59906  		{ "db_get",            unqliteBuiltin_db_fetch_next     },
 59907  		{ "db_fetch_by_id",    unqliteBuiltin_db_fetch_by_id    },
 59908  		{ "db_get_by_id",      unqliteBuiltin_db_fetch_by_id    },
 59909  		{ "db_fetch_all",      unqliteBuiltin_db_fetch_all      },
 59910  		{ "db_get_all",        unqliteBuiltin_db_fetch_all      },
 59911  		{ "db_last_record_id", unqliteBuiltin_db_last_record_id },
 59912  		{ "db_current_record_id", unqliteBuiltin_db_current_record_id },
 59913  		{ "db_reset_record_cursor", unqliteBuiltin_db_reset_record_cursor },
 59914  		{ "db_total_records",  unqliteBuiltin_db_total_records  },
 59915  		{ "db_creation_date",  unqliteBuiltin_db_creation_date  },
 59916  		{ "db_store",          unqliteBuiltin_db_store          },
 59917  		{ "db_put",            unqliteBuiltin_db_store          },
 59918  		{ "db_drop_collection", unqliteBuiltin_db_drop_col      },
 59919  		{ "collection_delete", unqliteBuiltin_db_drop_col       },
 59920  		{ "db_drop_record",    unqliteBuiltin_db_drop_record    },
 59921  		{ "db_set_schema",     unqliteBuiltin_db_set_schema     },
 59922  		{ "db_get_schema",     unqliteBuiltin_db_get_schema     },
 59923  		{ "db_begin",          unqliteBuiltin_db_begin          },
 59924  		{ "db_commit",         unqliteBuiltin_db_commit         },
 59925  		{ "db_rollback",       unqliteBuiltin_db_rollback       },
 59926  	};
 59927  	int rc = UNQLITE_OK;
 59928  	sxu32 n;
 59929  	/* Register the unQLite functions defined above in the Jx9 call table */
 59930  	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltin) ; ++n ){
 59931  		rc = jx9_create_function(pVm->pJx9Vm,aBuiltin[n].zName,aBuiltin[n].xFunc,pVm);
 59932  	}
 59933  	return rc;
 59934  }
 59935  /* END-OF-IMPLEMENTATION: unqlite@embedded@symisc 34-09-46 */
 59936  /*
 59937   * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
 59938   * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
 59939   * Version 1.1.6
 59940   * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
 59941   * please contact Symisc Systems via:
 59942   *       legal@symisc.net
 59943   *       licensing@symisc.net
 59944   *       contact@symisc.net
 59945   * or visit:
 59946   *      http://unqlite.org/licensing.html
 59947   */
 59948  /*
 59949   * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
 59950   * All rights reserved.
 59951   *
 59952   * Redistribution and use in source and binary forms, with or without
 59953   * modification, are permitted provided that the following conditions
 59954   * are met:
 59955   * 1. Redistributions of source code must retain the above copyright
 59956   *    notice, this list of conditions and the following disclaimer.
 59957   * 2. Redistributions in binary form must reproduce the above copyright
 59958   *    notice, this list of conditions and the following disclaimer in the
 59959   *    documentation and/or other materials provided with the distribution.
 59960   *
 59961   * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
 59962   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 59963   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 59964   * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
 59965   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 59966   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 59967   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 59968   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 59969   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 59970   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 59971   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 59972   */