1 /* license.txt */
2 /*
3  * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
18  * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 
29  #ifndef UNQLITE_AMALGAMATION
30  #define UNQLITE_AMALGAMATION
31  #define JX9_AMALGAMATION
32  /* Marker for routines not intended for external use */
33  #define JX9_PRIVATE static
34  #endif /* UNQLITE_AMALGAMATION */
35 /* jx9.h */
36 /* This file was automatically generated.  Do not edit (except for compile time directive)! */
37 #ifndef _JX9H_
38 #define _JX9H_
39 /*
40  * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
41  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
42  * Version 1.7.2
43  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
44  * please contact Symisc Systems via:
45  *       legal@symisc.net
46  *       licensing@symisc.net
47  *       contact@symisc.net
48  * or visit:
49  *      http://jx9.symisc.net/
50  */
51 /*
52  * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
53  *
54  * Redistribution and use in source and binary forms, with or without
55  * modification, are permitted provided that the following conditions
56  * are met:
57  * 1. Redistributions of source code must retain the above copyright
58  *    notice, this list of conditions and the following disclaimer.
59  * 2. Redistributions in binary form must reproduce the above copyright
60  *    notice, this list of conditions and the following disclaimer in the
61  *    documentation and/or other materials provided with the distribution.
62  * 3. Redistributions in any form must be accompanied by information on
63  *    how to obtain complete source code for the JX9 engine and any
64  *    accompanying software that uses the JX9 engine software.
65  *    The source code must either be included in the distribution
66  *    or be available for no more than the cost of distribution plus
67  *    a nominal fee, and must be freely redistributable under reasonable
68  *    conditions. For an executable file, complete source code means
69  *    the source code for all modules it contains.It does not include
70  *    source code for modules or files that typically accompany the major
71  *    components of the operating system on which the executable file runs.
72  *
73  * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
74  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
75  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
76  * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
77  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
78  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
79  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
80  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
81  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
82  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
83  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
84  */
85  /* $SymiscID: jx9.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable <chm@symisc.net> $ */
86 #include "unqlite.h"
87 /*
88  * Compile time engine version, signature, identification in the symisc source tree
89  * and copyright notice.
90  * Each macro have an equivalent C interface associated with it that provide the same
91  * information but are associated with the library instead of the header file.
92  * Refer to [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and
93  * [jx9_lib_copyright()] for more information.
94  */
95 /*
96  * The JX9_VERSION C preprocessor macroevaluates to a string literal
97  * that is the jx9 version in the format "X.Y.Z" where X is the major
98  * version number and Y is the minor version number and Z is the release
99  * number.
100  */
101 #define JX9_VERSION "1.7.2"
102 /*
103  * The JX9_VERSION_NUMBER C preprocessor macro resolves to an integer
104  * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
105  * numbers used in [JX9_VERSION].
106  */
107 #define JX9_VERSION_NUMBER 1007002
108 /*
109  * The JX9_SIG C preprocessor macro evaluates to a string
110  * literal which is the public signature of the jx9 engine.
111  * This signature could be included for example in a host-application
112  * generated Server MIME header as follows:
113  *   Server: YourWebServer/x.x Jx9/x.x.x \r\n
114  */
115 #define JX9_SIG "Jx9/1.7.2"
116 /*
117  * JX9 identification in the Symisc source tree:
118  * Each particular check-in of a particular software released
119  * by symisc systems have an unique identifier associated with it.
120  * This macro hold the one associated with jx9.
121  */
122 #define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7"
123 /*
124  * Copyright notice.
125  * If you have any questions about the licensing situation, please
126  * visit http://jx9.symisc.net/licensing.html
127  * or contact Symisc Systems via:
128  *   legal@symisc.net
129  *   licensing@symisc.net
130  *   contact@symisc.net
131  */
132 #define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/"
133 
134 /* Forward declaration to public objects */
135 typedef struct jx9_io_stream jx9_io_stream;
136 typedef struct jx9_context jx9_context;
137 typedef struct jx9_value jx9_value;
138 typedef struct jx9_vfs jx9_vfs;
139 typedef struct jx9_vm jx9_vm;
140 typedef struct jx9 jx9;
141 
142 #include "unqlite.h"
143 
144 #if !defined( UNQLITE_ENABLE_JX9_HASH_FUNC )
145 #define JX9_DISABLE_HASH_FUNC
146 #endif /* UNQLITE_ENABLE_JX9_HASH_FUNC */
147 #ifdef UNQLITE_ENABLE_THREADS
148 #define JX9_ENABLE_THREADS
149 #endif /* UNQLITE_ENABLE_THREADS */
150 /* Standard JX9 return values */
151 #define JX9_OK      SXRET_OK      /* Successful result */
152 /* beginning-of-error-codes */
153 #define JX9_NOMEM   UNQLITE_NOMEM     /* Out of memory */
154 #define JX9_ABORT   UNQLITE_ABORT   /* Foreign Function request operation abort/Another thread have released this instance */
155 #define JX9_IO_ERR  UNQLITE_IOERR      /* IO error */
156 #define JX9_CORRUPT UNQLITE_CORRUPT /* Corrupt pointer/Unknown configuration option */
157 #define JX9_LOOKED  UNQLITE_LOCKED  /* Forbidden Operation */
158 #define JX9_COMPILE_ERR UNQLITE_COMPILE_ERR /* Compilation error */
159 #define JX9_VM_ERR      UNQLITE_VM_ERR      /* Virtual machine error */
160 /* end-of-error-codes */
161 /*
162  * If compiling for a processor that lacks floating point
163  * support, substitute integer for floating-point.
164  */
165 #ifdef JX9_OMIT_FLOATING_POINT
166 typedef sxi64 jx9_real;
167 #else
168 typedef double jx9_real;
169 #endif
170 typedef sxi64 jx9_int64;
171 /*
172  * Engine Configuration Commands.
173  *
174  * The following set of constants are the available configuration verbs that can
175  * be used by the host-application to configure the JX9 engine.
176  * These constants must be passed as the second argument to the [jx9_config()]
177  * interface.
178  * Each options require a variable number of arguments.
179  * The [jx9_config()] interface will return JX9_OK on success, any other
180  * return value indicates failure.
181  * For a full discussion on the configuration verbs and their expected
182  * parameters, please refer to this page:
183  *      http://jx9.symisc.net/c_api_func.html#jx9_config
184  */
185 #define JX9_CONFIG_ERR_ABORT     1  /* RESERVED FOR FUTURE USE */
186 #define JX9_CONFIG_ERR_LOG       2  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
187 /*
188  * Virtual Machine Configuration Commands.
189  *
190  * The following set of constants are the available configuration verbs that can
191  * be used by the host-application to configure the JX9 Virtual machine.
192  * These constants must be passed as the second argument to the [jx9_vm_config()]
193  * interface.
194  * Each options require a variable number of arguments.
195  * The [jx9_vm_config()] interface will return JX9_OK on success, any other return
196  * value indicates failure.
197  * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install
198  * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register
199  * a HTTP request and JX9_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
200  * For a full discussion on the configuration verbs and their expected parameters, please
201  * refer to this page:
202  *      http://jx9.symisc.net/c_api_func.html#jx9_vm_config
203  */
204 #define JX9_VM_CONFIG_OUTPUT           UNQLITE_VM_CONFIG_OUTPUT  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
205 #define JX9_VM_CONFIG_IMPORT_PATH      UNQLITE_VM_CONFIG_IMPORT_PATH  /* ONE ARGUMENT: const char *zIncludePath */
206 #define JX9_VM_CONFIG_ERR_REPORT       UNQLITE_VM_CONFIG_ERR_REPORT  /* NO ARGUMENTS: Report all run-time errors in the VM output */
207 #define JX9_VM_CONFIG_RECURSION_DEPTH  UNQLITE_VM_CONFIG_RECURSION_DEPTH  /* ONE ARGUMENT: int nMaxDepth */
208 #define JX9_VM_OUTPUT_LENGTH           UNQLITE_VM_OUTPUT_LENGTH  /* ONE ARGUMENT: unsigned int *pLength */
209 #define JX9_VM_CONFIG_CREATE_VAR       UNQLITE_VM_CONFIG_CREATE_VAR  /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
210 #define JX9_VM_CONFIG_HTTP_REQUEST     UNQLITE_VM_CONFIG_HTTP_REQUEST  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
211 #define JX9_VM_CONFIG_SERVER_ATTR      UNQLITE_VM_CONFIG_SERVER_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
212 #define JX9_VM_CONFIG_ENV_ATTR         UNQLITE_VM_CONFIG_ENV_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
213 #define JX9_VM_CONFIG_EXEC_VALUE       UNQLITE_VM_CONFIG_EXEC_VALUE  /* ONE ARGUMENT: jx9_value **ppValue */
214 #define JX9_VM_CONFIG_IO_STREAM        UNQLITE_VM_CONFIG_IO_STREAM  /* ONE ARGUMENT: const jx9_io_stream *pStream */
215 #define JX9_VM_CONFIG_ARGV_ENTRY       UNQLITE_VM_CONFIG_ARGV_ENTRY  /* ONE ARGUMENT: const char *zValue */
216 #define JX9_VM_CONFIG_EXTRACT_OUTPUT   UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
217 /*
218  * Global Library Configuration Commands.
219  *
220  * The following set of constants are the available configuration verbs that can
221  * be used by the host-application to configure the whole library.
222  * These constants must be passed as the first argument to the [jx9_lib_config()]
223  * interface.
224  * Each options require a variable number of arguments.
225  * The [jx9_lib_config()] interface will return JX9_OK on success, any other return
226  * value indicates failure.
227  * Notes:
228  * The default configuration is recommended for most applications and so the call to
229  * [jx9_lib_config()] is usually not necessary. It is provided to support rare
230  * applications with unusual needs.
231  * The [jx9_lib_config()] interface is not threadsafe. The application must insure that
232  * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()]
233  * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library
234  * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown
235  * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()]
236  * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED.
237  * For a full discussion on the configuration verbs and their expected parameters, please
238  * refer to this page:
239  *      http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces
240  */
241 #define JX9_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
242 #define JX9_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
243 #define JX9_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
244 #define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */
245 #define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */
246 #define JX9_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const jx9_vfs *pVfs */
247 /*
248  * Call Context - Error Message Serverity Level.
249  */
250 #define JX9_CTX_ERR      UNQLITE_CTX_ERR      /* Call context error such as unexpected number of arguments, invalid types and so on. */
251 #define JX9_CTX_WARNING  UNQLITE_CTX_WARNING  /* Call context Warning */
252 #define JX9_CTX_NOTICE   UNQLITE_CTX_NOTICE   /* Call context Notice */
253 /* Current VFS structure version*/
254 #define JX9_VFS_VERSION 2
255 /*
256  * JX9 Virtual File System (VFS).
257  *
258  * An instance of the jx9_vfs object defines the interface between the JX9 core
259  * and the underlying operating system. The "vfs" in the name of the object stands
260  * for "virtual file system". The vfs is used to implement JX9 system functions
261  * such as mkdir(), chdir(), stat(), get_user_name() and many more.
262  * The value of the iVersion field is initially 2 but may be larger in future versions
263  * of JX9.
264  * Additional fields may be appended to this object when the iVersion value is increased.
265  * Only a single vfs can be registered within the JX9 core. Vfs registration is done
266  * using the jx9_lib_config() interface with a configuration verb set to JX9_LIB_CONFIG_VFS.
267  * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to
268  * worry about registering and installing a vfs since JX9 come with a built-in vfs for these
269  * platforms which implement most the methods defined below.
270  * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must
271  * register their own vfs in order to be able to use and call JX9 system functions.
272  * Also note that the jx9_compile_file() interface depend on the xMmap() method of the underlying
273  * vfs which mean that this method must be available (Always the case using the built-in VFS)
274  * in order to use this interface.
275  * Developers wishing to implement their own vfs an contact symisc systems to obtain
276  * the JX9 VFS C/C++ Specification manual.
277  */
278 struct jx9_vfs
279 {
280 	const char *zName;  /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */
281 	int iVersion;       /* Current VFS structure version [default 2] */
282 	/* Directory functions */
283 	int (*xChdir)(const char *);                     /* Change directory */
284 	int (*xChroot)(const char *);                    /* Change the root directory */
285 	int (*xGetcwd)(jx9_context *);                   /* Get the current working directory */
286 	int (*xMkdir)(const char *, int, int);             /* Make directory */
287 	int (*xRmdir)(const char *);                     /* Remove directory */
288 	int (*xIsdir)(const char *);                     /* Tells whether the filename is a directory */
289 	int (*xRename)(const char *, const char *);       /* Renames a file or directory */
290 	int (*xRealpath)(const char *, jx9_context *);    /* Return canonicalized absolute pathname*/
291 	/* Systems functions */
292 	int (*xSleep)(unsigned int);                     /* Delay execution in microseconds */
293 	int (*xUnlink)(const char *);                    /* Deletes a file */
294 	int (*xFileExists)(const char *);                /* Checks whether a file or directory exists */
295 	int (*xChmod)(const char *, int);                 /* Changes file mode */
296 	int (*xChown)(const char *, const char *);        /* Changes file owner */
297 	int (*xChgrp)(const char *, const char *);        /* Changes file group */
298 	jx9_int64 (*xFreeSpace)(const char *);           /* Available space on filesystem or disk partition */
299 	jx9_int64 (*xTotalSpace)(const char *);          /* Total space on filesystem or disk partition */
300 	jx9_int64 (*xFileSize)(const char *);            /* Gets file size */
301 	jx9_int64 (*xFileAtime)(const char *);           /* Gets last access time of file */
302 	jx9_int64 (*xFileMtime)(const char *);           /* Gets file modification time */
303 	jx9_int64 (*xFileCtime)(const char *);           /* Gets inode change time of file */
304 	int (*xStat)(const char *, jx9_value *, jx9_value *);   /* Gives information about a file */
305 	int (*xlStat)(const char *, jx9_value *, jx9_value *);  /* Gives information about a file */
306 	int (*xIsfile)(const char *);                    /* Tells whether the filename is a regular file */
307 	int (*xIslink)(const char *);                    /* Tells whether the filename is a symbolic link */
308 	int (*xReadable)(const char *);                  /* Tells whether a file exists and is readable */
309 	int (*xWritable)(const char *);                  /* Tells whether the filename is writable */
310 	int (*xExecutable)(const char *);                /* Tells whether the filename is executable */
311 	int (*xFiletype)(const char *, jx9_context *);    /* Gets file type [i.e: fifo, dir, file..] */
312 	int (*xGetenv)(const char *, jx9_context *);      /* Gets the value of an environment variable */
313 	int (*xSetenv)(const char *, const char *);       /* Sets the value of an environment variable */
314 	int (*xTouch)(const char *, jx9_int64, jx9_int64); /* Sets access and modification time of file */
315 	int (*xMmap)(const char *, void **, jx9_int64 *);  /* Read-only memory map of the whole file */
316 	void (*xUnmap)(void *, jx9_int64);                /* Unmap a memory view */
317 	int (*xLink)(const char *, const char *, int);     /* Create hard or symbolic link */
318 	int (*xUmask)(int);                              /* Change the current umask */
319 	void (*xTempDir)(jx9_context *);                 /* Get path of the temporary directory */
320 	unsigned int (*xProcessId)(void);                /* Get running process ID */
321 	int (*xUid)(void);                               /* user ID of the process */
322 	int (*xGid)(void);                               /* group ID of the process */
323 	void (*xUsername)(jx9_context *);                /* Running username */
324 	int (*xExec)(const char *, jx9_context *);        /* Execute an external program */
325 };
326 /* Current JX9 IO stream structure version. */
327 #define JX9_IO_STREAM_VERSION 1
328 /*
329  * Possible open mode flags that can be passed to the xOpen() routine
330  * of the underlying IO stream device .
331  * Refer to the JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html)
332  * for additional information.
333  */
334 #define JX9_IO_OPEN_RDONLY   0x001  /* Read-only open */
335 #define JX9_IO_OPEN_WRONLY   0x002  /* Write-only open */
336 #define JX9_IO_OPEN_RDWR     0x004  /* Read-write open. */
337 #define JX9_IO_OPEN_CREATE   0x008  /* If the file does not exist it will be created */
338 #define JX9_IO_OPEN_TRUNC    0x010  /* Truncate the file to zero length */
339 #define JX9_IO_OPEN_APPEND   0x020  /* Append mode.The file offset is positioned at the end of the file */
340 #define JX9_IO_OPEN_EXCL     0x040  /* Ensure that this call creates the file, the file must not exist before */
341 #define JX9_IO_OPEN_BINARY   0x080  /* Simple hint: Data is binary */
342 #define JX9_IO_OPEN_TEMP     0x100  /* Simple hint: Temporary file */
343 #define JX9_IO_OPEN_TEXT     0x200  /* Simple hint: Data is textual */
344 /*
345  * JX9 IO Stream Device.
346  *
347  * An instance of the jx9_io_stream object defines the interface between the JX9 core
348  * and the underlying stream device.
349  * A stream is a smart mechanism for generalizing file, network, data compression
350  * and other IO operations which share a common set of functions using an abstracted
351  * unified interface.
352  * A stream device is additional code which tells the stream how to handle specific
353  * protocols/encodings. For example, the http device knows how to translate a URL
354  * into an HTTP/1.1 request for a file on a remote server.
355  * JX9 come with two built-in IO streams device:
356  * The file:// stream which perform very efficient disk IO and the jx9:// stream
357  * which is a special stream that allow access various I/O streams (See the JX9 official
358  * documentation for more information on this stream).
359  * A stream is referenced as: scheme://target
360  * scheme(string) - The name of the wrapper to be used. Examples include: file, http, https, ftp,
361  * ftps, compress.zlib, compress.bz2, and jx9. If no wrapper is specified, the function default
362  * is used (typically file://).
363  * target - Depends on the device used. For filesystem related streams this is typically a path
364  * and filename of the desired file.For network related streams this is typically a hostname, often
365  * with a path appended.
366  * IO stream devices are registered using a call to jx9_vm_config() with a configuration verb
367  * set to JX9_VM_CONFIG_IO_STREAM.
368  * Currently the JX9 development team is working on the implementation of the http:// and ftp://
369  * IO stream protocols. These devices will be available in the next major release of the JX9 engine.
370  * Developers wishing to implement their own IO stream devices must understand and follow
371  * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html).
372  */
373 struct jx9_io_stream
374 {
375 	const char *zName;                                     /* Underlying stream name [i.e: file/http/zip/jx9, ..] */
376 	int iVersion;                                          /* IO stream structure version [default 1]*/
377 	int  (*xOpen)(const char *, int, jx9_value *, void **);   /* Open handle*/
378 	int  (*xOpenDir)(const char *, jx9_value *, void **);    /* Open directory handle */
379 	void (*xClose)(void *);                                /* Close file handle */
380 	void (*xCloseDir)(void *);                             /* Close directory handle */
381 	jx9_int64 (*xRead)(void *, void *, jx9_int64);           /* Read from the open stream */
382 	int (*xReadDir)(void *, jx9_context *);                 /* Read entry from directory handle */
383 	jx9_int64 (*xWrite)(void *, const void *, jx9_int64);    /* Write to the open stream */
384 	int (*xSeek)(void *, jx9_int64, int);                    /* Seek on the open stream */
385 	int (*xLock)(void *, int);                              /* Lock/Unlock the open stream */
386 	void (*xRewindDir)(void *);                            /* Rewind directory handle */
387 	jx9_int64 (*xTell)(void *);                            /* Current position of the stream  read/write pointer */
388 	int (*xTrunc)(void *, jx9_int64);                       /* Truncates the open stream to a given length */
389 	int (*xSync)(void *);                                  /* Flush open stream data */
390 	int (*xStat)(void *, jx9_value *, jx9_value *);          /* Stat an open stream handle */
391 };
392 /*
393  * C-API-REF: Please refer to the official documentation for interfaces
394  * purpose and expected parameters.
395  */
396 /* Engine Handling Interfaces */
397 JX9_PRIVATE int jx9_init(jx9 **ppEngine);
398 /*JX9_PRIVATE int jx9_config(jx9 *pEngine, int nConfigOp, ...);*/
399 JX9_PRIVATE int jx9_release(jx9 *pEngine);
400 /* Compile Interfaces */
401 JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
402 JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
403 /* Virtual Machine Handling Interfaces */
404 JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
405 /*JX9_PRIVATE int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);*/
406 /*JX9_PRIVATE jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);*/
407 /*JX9_PRIVATE int jx9_vm_reset(jx9_vm *pVm);*/
408 JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm);
409 /*JX9_PRIVATE int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);*/
410 /* In-process Extending Interfaces */
411 JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
412 /*JX9_PRIVATE int jx9_delete_function(jx9_vm *pVm, const char *zName);*/
413 JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
414 /*JX9_PRIVATE int jx9_delete_constant(jx9_vm *pVm, const char *zName);*/
415 /* Foreign Function Parameter Values */
416 JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue);
417 JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue);
418 JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue);
419 JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue);
420 JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
421 JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue);
422 JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
423 /* Setting The Result Of A Foreign Function */
424 JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue);
425 JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
426 JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool);
427 JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value);
428 JX9_PRIVATE int jx9_result_null(jx9_context *pCtx);
429 JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
430 JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
431 JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
432 JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData);
433 /* Call Context Handling Interfaces */
434 JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
435 /*JX9_PRIVATE int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);*/
436 JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
437 JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
438 JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx);
439 JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
440 JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx);
441 JX9_PRIVATE int    jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
442 JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx);
443 JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx);
444 JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
445 JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx);
446 /* Call Context Memory Management Interfaces */
447 JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
448 JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
449 JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
450 /* On Demand Dynamically Typed Value Object allocation interfaces */
451 JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm);
452 JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm);
453 JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
454 JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
455 JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx);
456 JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
457 /* Dynamically Typed Value Object Management Interfaces */
458 JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue);
459 JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
460 JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool);
461 JX9_PRIVATE int jx9_value_null(jx9_value *pVal);
462 JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value);
463 JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
464 JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
465 JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal);
466 JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData);
467 JX9_PRIVATE int jx9_value_release(jx9_value *pVal);
468 /* JSON Array/Object Management Interfaces */
469 JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
470 JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
471 JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
472 JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
473 JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray);
474 /* Dynamically Typed Value Object Query Interfaces */
475 JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal);
476 JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal);
477 JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal);
478 JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal);
479 JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal);
480 JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal);
481 JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal);
482 JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal);
483 JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal);
484 JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal);
485 JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal);
486 JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal);
487 /* Global Library Management Interfaces */
488 /*JX9_PRIVATE int jx9_lib_init(void);*/
489 JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...);
490 JX9_PRIVATE int jx9_lib_shutdown(void);
491 /*JX9_PRIVATE int jx9_lib_is_threadsafe(void);*/
492 /*JX9_PRIVATE const char * jx9_lib_version(void);*/
493 JX9_PRIVATE const char * jx9_lib_signature(void);
494 /*JX9_PRIVATE const char * jx9_lib_ident(void);*/
495 /*JX9_PRIVATE const char * jx9_lib_copyright(void);*/
496 
497 #endif /* _JX9H_ */
498 
499 /* jx9Int.h */
500 /*
501  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
502  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
503  * Version 1.7.2
504  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
505  * please contact Symisc Systems via:
506  *       legal@symisc.net
507  *       licensing@symisc.net
508  *       contact@symisc.net
509  * or visit:
510  *      http://jx9.symisc.net/
511  */
512  /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel <chm@symisc.net> $ */
513 #ifndef __JX9INT_H__
514 #define __JX9INT_H__
515 /* Internal interface definitions for JX9. */
516 #ifdef JX9_AMALGAMATION
517 #ifndef JX9_PRIVATE
518 /* Marker for routines not intended for external use */
519 #define JX9_PRIVATE static
520 #endif /* JX9_PRIVATE */
521 #else
522 #define JX9_PRIVATE
523 #include "jx9.h"
524 #endif
525 #ifndef JX9_PI
526 /* Value of PI */
527 #define JX9_PI 3.1415926535898
528 #endif
529 /*
530  * Constants for the largest and smallest possible 64-bit signed integers.
531  * These macros are designed to work correctly on both 32-bit and 64-bit
532  * compilers.
533  */
534 #ifndef LARGEST_INT64
535 #define LARGEST_INT64  (0xffffffff|(((sxi64)0x7fffffff)<<32))
536 #endif
537 #ifndef SMALLEST_INT64
538 #define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64)
539 #endif
540 /* Forward declaration of private structures */
541 typedef struct jx9_foreach_info   jx9_foreach_info;
542 typedef struct jx9_foreach_step   jx9_foreach_step;
543 typedef struct jx9_hashmap_node   jx9_hashmap_node;
544 typedef struct jx9_hashmap        jx9_hashmap;
545 /* Symisc Standard types */
546 #if !defined(SYMISC_STD_TYPES)
547 #define SYMISC_STD_TYPES
548 #ifdef __WINNT__
549 /* Disable nuisance warnings on Borland compilers */
550 #if defined(__BORLANDC__)
551 #pragma warn -rch /* unreachable code */
552 #pragma warn -ccc /* Condition is always true or false */
553 #pragma warn -aus /* Assigned value is never used */
554 #pragma warn -csu /* Comparing signed and unsigned */
555 #pragma warn -spa /* Suspicious pointer arithmetic */
556 #endif
557 #endif
558 typedef signed char        sxi8; /* signed char */
559 typedef unsigned char      sxu8; /* unsigned char */
560 typedef signed short int   sxi16; /* 16 bits(2 bytes) signed integer */
561 typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */
562 typedef int                sxi32; /* 32 bits(4 bytes) integer */
563 typedef unsigned int       sxu32; /* 32 bits(4 bytes) unsigned integer */
564 typedef long               sxptr;
565 typedef unsigned long      sxuptr;
566 typedef long               sxlong;
567 typedef unsigned long      sxulong;
568 typedef sxi32              sxofft;
569 typedef sxi64              sxofft64;
570 typedef long double	       sxlongreal;
571 typedef double             sxreal;
572 #define SXI8_HIGH       0x7F
573 #define SXU8_HIGH       0xFF
574 #define SXI16_HIGH      0x7FFF
575 #define SXU16_HIGH      0xFFFF
576 #define SXI32_HIGH      0x7FFFFFFF
577 #define SXU32_HIGH      0xFFFFFFFF
578 #define SXI64_HIGH      0x7FFFFFFFFFFFFFFF
579 #define SXU64_HIGH      0xFFFFFFFFFFFFFFFF
580 #if !defined(TRUE)
581 #define TRUE 1
582 #endif
583 #if !defined(FALSE)
584 #define FALSE 0
585 #endif
586 /*
587  * The following macros are used to cast pointers to integers and
588  * integers to pointers.
589  */
590 #if defined(__PTRDIFF_TYPE__)
591 # define SX_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
592 # define SX_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
593 #elif !defined(__GNUC__)
594 # define SX_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
595 # define SX_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
596 #else
597 # define SX_INT_TO_PTR(X)  ((void*)(X))
598 # define SX_PTR_TO_INT(X)  ((int)(X))
599 #endif
600 #define SXMIN(a, b)  ((a < b) ? (a) : (b))
601 #define SXMAX(a, b)  ((a < b) ? (b) : (a))
602 #endif /* SYMISC_STD_TYPES */
603 /* Symisc Run-time API private definitions */
604 #if !defined(SYMISC_PRIVATE_DEFS)
605 #define SYMISC_PRIVATE_DEFS
606 
607 typedef sxi32 (*ProcRawStrCmp)(const SyString *, const SyString *);
608 #define SyStringData(RAW)	((RAW)->zString)
609 #define SyStringLength(RAW)	((RAW)->nByte)
610 #define SyStringInitFromBuf(RAW, ZBUF, NLEN){\
611 	(RAW)->zString 	= (const char *)ZBUF;\
612 	(RAW)->nByte	= (sxu32)(NLEN);\
613 }
614 #define SyStringUpdatePtr(RAW, NBYTES){\
615 	if( NBYTES > (RAW)->nByte ){\
616 		(RAW)->nByte = 0;\
617 	}else{\
618 		(RAW)->zString += NBYTES;\
619 		(RAW)->nByte -= NBYTES;\
620 	}\
621 }
622 #define SyStringDupPtr(RAW1, RAW2)\
623 	(RAW1)->zString = (RAW2)->zString;\
624 	(RAW1)->nByte = (RAW2)->nByte;
625 
626 #define SyStringTrimLeadingChar(RAW, CHAR)\
627 	while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\
628 			(RAW)->zString++;\
629 			(RAW)->nByte--;\
630 	}
631 #define SyStringTrimTrailingChar(RAW, CHAR)\
632 	while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\
633 		(RAW)->nByte--;\
634 	}
635 #define SyStringCmp(RAW1, RAW2, xCMP)\
636 	(((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte))
637 
638 #define SyStringCmp2(RAW1, RAW2, xCMP)\
639 	(((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte))
640 
641 #define SyStringCharCmp(RAW, CHAR) \
642 	(((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char)))
643 
644 #define SX_ADDR(PTR)    ((sxptr)PTR)
645 #define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
646 #define SXUNUSED(P)	(P = 0)
647 #define	SX_EMPTY(PTR)   (PTR == 0)
648 #define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 )
649 typedef struct SyMemBackend SyMemBackend;
650 typedef struct SyBlob SyBlob;
651 typedef struct SySet SySet;
652 /* Standard function signatures */
653 typedef sxi32 (*ProcCmp)(const void *, const void *, sxu32);
654 typedef sxi32 (*ProcPatternMatch)(const char *, sxu32, const char *, sxu32, sxu32 *);
655 typedef sxi32 (*ProcSearch)(const void *, sxu32, const void *, sxu32, ProcCmp, sxu32 *);
656 typedef sxu32 (*ProcHash)(const void *, sxu32);
657 typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32);
658 typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp);
659 #define MACRO_LIST_PUSH(Head, Item)\
660 	Item->pNext = Head;\
661 	Head = Item;
662 #define MACRO_LD_PUSH(Head, Item)\
663 	if( Head == 0 ){\
664 		Head = Item;\
665 	}else{\
666 		Item->pNext = Head;\
667 		Head->pPrev = Item;\
668 		Head = Item;\
669 	}
670 #define MACRO_LD_REMOVE(Head, Item)\
671 	if( Head == Item ){\
672 		Head = Head->pNext;\
673 	}\
674 	if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\
675 	if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;}
676 /*
677  * A generic dynamic set.
678  */
679 struct SySet
680 {
681 	SyMemBackend *pAllocator; /* Memory backend */
682 	void *pBase;              /* Base pointer */
683 	sxu32 nUsed;              /* Total number of used slots  */
684 	sxu32 nSize;              /* Total number of available slots */
685 	sxu32 eSize;              /* Size of a single slot */
686 	sxu32 nCursor;	          /* Loop cursor */
687 	void *pUserData;          /* User private data associated with this container */
688 };
689 #define SySetBasePtr(S)           ((S)->pBase)
690 #define SySetBasePtrJump(S, OFFT)  (&((char *)(S)->pBase)[OFFT*(S)->eSize])
691 #define SySetUsed(S)              ((S)->nUsed)
692 #define SySetSize(S)              ((S)->nSize)
693 #define SySetElemSize(S)          ((S)->eSize)
694 #define SySetCursor(S)            ((S)->nCursor)
695 #define SySetGetAllocator(S)      ((S)->pAllocator)
696 #define SySetSetUserData(S, DATA)  ((S)->pUserData = DATA)
697 #define SySetGetUserData(S)       ((S)->pUserData)
698 /*
699  * A variable length containers for generic data.
700  */
701 struct SyBlob
702 {
703 	SyMemBackend *pAllocator; /* Memory backend */
704 	void   *pBlob;	          /* Base pointer */
705 	sxu32  nByte;	          /* Total number of used bytes */
706 	sxu32  mByte;	          /* Total number of available bytes */
707 	sxu32  nFlags;	          /* Blob internal flags, see below */
708 };
709 #define SXBLOB_LOCKED	0x01	/* Blob is locked [i.e: Cannot auto grow] */
710 #define SXBLOB_STATIC	0x02	/* Not allocated from heap   */
711 #define SXBLOB_RDONLY   0x04    /* Read-Only data */
712 
713 #define SyBlobFreeSpace(BLOB)	 ((BLOB)->mByte - (BLOB)->nByte)
714 #define SyBlobLength(BLOB)	     ((BLOB)->nByte)
715 #define SyBlobData(BLOB)	     ((BLOB)->pBlob)
716 #define SyBlobCurData(BLOB)	     ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte]))
717 #define SyBlobDataAt(BLOB, OFFT)	 ((void *)(&((char *)(BLOB)->pBlob)[OFFT]))
718 #define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator)
719 
720 #define SXMEM_POOL_INCR			3
721 #define SXMEM_POOL_NBUCKETS		12
722 #define SXMEM_BACKEND_MAGIC	0xBAC3E67D
723 #define SXMEM_BACKEND_CORRUPT(BACKEND)	(BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC)
724 
725 #define SXMEM_BACKEND_RETRY	3
726 /* A memory backend subsystem is defined by an instance of the following structures */
727 typedef union SyMemHeader SyMemHeader;
728 typedef struct SyMemBlock SyMemBlock;
729 struct SyMemBlock
730 {
731 	SyMemBlock *pNext, *pPrev; /* Chain of allocated memory blocks */
732 #ifdef UNTRUST
733 	sxu32 nGuard;             /* magic number associated with each valid block, so we
734 							   * can detect misuse.
735 							   */
736 #endif
737 };
738 /*
739  * Header associated with each valid memory pool block.
740  */
741 union SyMemHeader
742 {
743 	SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */
744 	sxu32 nBucket;      /* Bucket index in aPool[] */
745 };
746 struct SyMemBackend
747 {
748 	const SyMutexMethods *pMutexMethods; /* Mutex methods */
749 	const SyMemMethods *pMethods;  /* Memory allocation methods */
750 	SyMemBlock *pBlocks;           /* List of valid memory blocks */
751 	sxu32 nBlock;                  /* Total number of memory blocks allocated so far */
752 	ProcMemError xMemError;        /* Out-of memory callback */
753 	void *pUserData;               /* First arg to xMemError() */
754 	SyMutex *pMutex;               /* Per instance mutex */
755 	sxu32 nMagic;                  /* Sanity check against misuse */
756 	SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */
757 };
758 /* Mutex types */
759 #define SXMUTEX_TYPE_FAST	1
760 #define SXMUTEX_TYPE_RECURSIVE	2
761 #define SXMUTEX_TYPE_STATIC_1	3
762 #define SXMUTEX_TYPE_STATIC_2	4
763 #define SXMUTEX_TYPE_STATIC_3	5
764 #define SXMUTEX_TYPE_STATIC_4	6
765 #define SXMUTEX_TYPE_STATIC_5	7
766 #define SXMUTEX_TYPE_STATIC_6	8
767 
768 #define SyMutexGlobalInit(METHOD){\
769 	if( (METHOD)->xGlobalInit ){\
770 	(METHOD)->xGlobalInit();\
771 	}\
772 }
773 #define SyMutexGlobalRelease(METHOD){\
774 	if( (METHOD)->xGlobalRelease ){\
775 	(METHOD)->xGlobalRelease();\
776 	}\
777 }
778 #define SyMutexNew(METHOD, TYPE)			(METHOD)->xNew(TYPE)
779 #define SyMutexRelease(METHOD, MUTEX){\
780 	if( MUTEX && (METHOD)->xRelease ){\
781 		(METHOD)->xRelease(MUTEX);\
782 	}\
783 }
784 #define SyMutexEnter(METHOD, MUTEX){\
785 	if( MUTEX ){\
786 	(METHOD)->xEnter(MUTEX);\
787 	}\
788 }
789 #define SyMutexTryEnter(METHOD, MUTEX){\
790 	if( MUTEX && (METHOD)->xTryEnter ){\
791 	(METHOD)->xTryEnter(MUTEX);\
792 	}\
793 }
794 #define SyMutexLeave(METHOD, MUTEX){\
795 	if( MUTEX ){\
796 	(METHOD)->xLeave(MUTEX);\
797 	}\
798 }
799 /* Comparison, byte swap, byte copy macros */
800 #define SX_MACRO_FAST_CMP(X1, X2, SIZE, RC){\
801 	register unsigned char *r1 = (unsigned char *)X1;\
802 	register unsigned char *r2 = (unsigned char *)X2;\
803 	register sxu32 LEN = SIZE;\
804 	for(;;){\
805 	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
806 	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
807 	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
808 	  if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
809 	}\
810 	RC = !LEN ? 0 : r1[0] - r2[0];\
811 }
812 #define	SX_MACRO_FAST_MEMCPY(SRC, DST, SIZ){\
813 	register unsigned char *xSrc = (unsigned char *)SRC;\
814 	register unsigned char *xDst = (unsigned char *)DST;\
815 	register sxu32 xLen = SIZ;\
816 	for(;;){\
817 	    if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
818 		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
819 		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
820 		if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
821 	}\
822 }
823 #define SX_MACRO_BYTE_SWAP(X, Y, Z){\
824 	register unsigned char *s = (unsigned char *)X;\
825 	register unsigned char *d = (unsigned char *)Y;\
826 	sxu32	ZLong = Z;  \
827 	sxi32 c; \
828 	for(;;){\
829 	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
830 	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
831 	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
832 	  if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
833 	}\
834 }
835 #define SX_MSEC_PER_SEC	(1000)			/* Millisec per seconds */
836 #define SX_USEC_PER_SEC	(1000000)		/* Microsec per seconds */
837 #define SX_NSEC_PER_SEC	(1000000000)	/* Nanosec per seconds */
838 #endif /* SYMISC_PRIVATE_DEFS */
839 /* Symisc Run-time API auxiliary definitions */
840 #if !defined(SYMISC_PRIVATE_AUX_DEFS)
841 #define SYMISC_PRIVATE_AUX_DEFS
842 
843 typedef struct SyHashEntry_Pr SyHashEntry_Pr;
844 typedef struct SyHashEntry SyHashEntry;
845 typedef struct SyHash SyHash;
846 /*
847  * Each public hashtable entry is represented by an instance
848  * of the following structure.
849  */
850 struct SyHashEntry
851 {
852 	const void *pKey; /* Hash key */
853 	sxu32 nKeyLen;    /* Key length */
854 	void *pUserData;  /* User private data */
855 };
856 #define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData)
857 #define SyHashEntryGetKey(ENTRY)      ((ENTRY)->pKey)
858 /* Each active hashtable is identified by an instance of the following structure */
859 struct SyHash
860 {
861 	SyMemBackend *pAllocator;         /* Memory backend */
862 	ProcHash xHash;                   /* Hash function */
863 	ProcCmp xCmp;                     /* Comparison function */
864 	SyHashEntry_Pr *pList, *pCurrent;  /* Linked list of hash entries user for linear traversal */
865 	sxu32 nEntry;                     /* Total number of entries */
866 	SyHashEntry_Pr **apBucket;        /* Hash buckets */
867 	sxu32 nBucketSize;                /* Current bucket size */
868 };
869 #define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */
870 #define SXHASH_FILL_FACTOR 3
871 /* Hash access macro */
872 #define SyHashFunc(HASH)		((HASH)->xHash)
873 #define SyHashCmpFunc(HASH)		((HASH)->xCmp)
874 #define SyHashTotalEntry(HASH)	((HASH)->nEntry)
875 #define SyHashGetPool(HASH)		((HASH)->pAllocator)
876 /*
877  * An instance of the following structure define a single context
878  * for an Pseudo Random Number Generator.
879  *
880  * Nothing in this file or anywhere else in the library does any kind of
881  * encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
882  * number generator) not as an encryption device.
883  * This implementation is taken from the SQLite3 source tree.
884  */
885 typedef struct SyPRNGCtx SyPRNGCtx;
886 struct SyPRNGCtx
887 {
888     sxu8 i, j;				/* State variables */
889     unsigned char s[256];   /* State variables */
890 	sxu16 nMagic;			/* Sanity check */
891  };
892 typedef sxi32 (*ProcRandomSeed)(void *, unsigned int, void *);
893 /* High resolution timer.*/
894 typedef struct sytime sytime;
895 struct sytime
896 {
897 	long tm_sec;	/* seconds */
898 	long tm_usec;	/* microseconds */
899 };
900 /* Forward declaration */
901 typedef struct SyStream SyStream;
902 typedef struct SyToken  SyToken;
903 typedef struct SyLex    SyLex;
904 /*
905  * Tokenizer callback signature.
906  */
907 typedef sxi32 (*ProcTokenizer)(SyStream *, SyToken *, void *, void *);
908 /*
909  * Each token in the input is represented by an instance
910  * of the following structure.
911  */
912 struct SyToken
913 {
914 	SyString sData;  /* Token text and length */
915 	sxu32 nType;     /* Token type */
916 	sxu32 nLine;     /* Token line number */
917 	void *pUserData; /* User private data associated with this token */
918 };
919 /*
920  * During tokenization, information about the state of the input
921  * stream is held in an instance of the following structure.
922  */
923 struct SyStream
924 {
925 	const unsigned char *zInput; /* Complete text of the input */
926 	const unsigned char *zText; /* Current input we are processing */
927 	const unsigned char *zEnd; /* End of input marker */
928 	sxu32  nLine; /* Total number of processed lines */
929 	sxu32  nIgn; /* Total number of ignored tokens */
930 	SySet *pSet; /* Token containers */
931 };
932 /*
933  * Each lexer is represented by an instance of the following structure.
934  */
935 struct SyLex
936 {
937 	SyStream sStream;         /* Input stream */
938 	ProcTokenizer xTokenizer; /* Tokenizer callback */
939 	void * pUserData;         /* Third argument to xTokenizer() */
940 	SySet *pTokenSet;         /* Token set */
941 };
942 #define SyLexTotalToken(LEX)    SySetTotalEntry(&(LEX)->aTokenSet)
943 #define SyLexTotalLines(LEX)    ((LEX)->sStream.nLine)
944 #define SyLexTotalIgnored(LEX)  ((LEX)->sStream.nIgn)
945 #define XLEX_IN_LEN(STREAM)     (sxu32)(STREAM->zEnd - STREAM->zText)
946 #endif /* SYMISC_PRIVATE_AUX_DEFS */
947 /*
948 ** Notes on UTF-8 (According to SQLite3 authors):
949 **
950 **   Byte-0    Byte-1    Byte-2    Byte-3    Value
951 **  0xxxxxxx                                 00000000 00000000 0xxxxxxx
952 **  110yyyyy  10xxxxxx                       00000000 00000yyy yyxxxxxx
953 **  1110zzzz  10yyyyyy  10xxxxxx             00000000 zzzzyyyy yyxxxxxx
954 **  11110uuu  10uuzzzz  10yyyyyy  10xxxxxx   000uuuuu zzzzyyyy yyxxxxxx
955 **
956 */
957 /*
958 ** Assuming zIn points to the first byte of a UTF-8 character,
959 ** advance zIn to point to the first byte of the next UTF-8 character.
960 */
961 #define SX_JMP_UTF8(zIn, zEnd)\
962 	while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; }
963 #define SX_WRITE_UTF8(zOut, c) {                       \
964   if( c<0x00080 ){                                     \
965     *zOut++ = (sxu8)(c&0xFF);                          \
966   }else if( c<0x00800 ){                               \
967     *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F);              \
968     *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
969   }else if( c<0x10000 ){                               \
970     *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F);             \
971     *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F);            \
972     *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
973   }else{                                               \
974     *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07);           \
975     *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F);           \
976     *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F);            \
977     *zOut++ = 0x80 + (sxu8)(c & 0x3F);                 \
978   }                                                    \
979 }
980 /* Rely on the standard ctype */
981 #include <ctype.h>
982 #define SyToUpper(c) toupper(c)
983 #define SyToLower(c) tolower(c)
984 #define SyisUpper(c) isupper(c)
985 #define SyisLower(c) islower(c)
986 #define SyisSpace(c) isspace(c)
987 #define SyisBlank(c) isspace(c)
988 #define SyisAlpha(c) isalpha(c)
989 #define SyisDigit(c) isdigit(c)
990 #define SyisHex(c)	 isxdigit(c)
991 #define SyisPrint(c) isprint(c)
992 #define SyisPunct(c) ispunct(c)
993 #define SyisSpec(c)	 iscntrl(c)
994 #define SyisCtrl(c)	 iscntrl(c)
995 #define SyisAscii(c) isascii(c)
996 #define SyisAlphaNum(c) isalnum(c)
997 #define SyisGraph(c)     isgraph(c)
998 #define SyDigToHex(c)    "0123456789ABCDEF"[c & 0x0F]
999 #define SyDigToInt(c)     ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 )
1000 #define SyCharToUpper(c)  ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c)
1001 #define SyCharToLower(c)  ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c)
1002 /* Remove white space/NUL byte from a raw string */
1003 #define SyStringLeftTrim(RAW)\
1004 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
1005 		(RAW)->nByte--;\
1006 		(RAW)->zString++;\
1007 	}
1008 #define SyStringLeftTrimSafe(RAW)\
1009 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
1010 		(RAW)->nByte--;\
1011 		(RAW)->zString++;\
1012 	}
1013 #define SyStringRightTrim(RAW)\
1014 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
1015 		(RAW)->nByte--;\
1016 	}
1017 #define SyStringRightTrimSafe(RAW)\
1018 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && \
1019 	(( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
1020 		(RAW)->nByte--;\
1021 	}
1022 
1023 #define SyStringFullTrim(RAW)\
1024 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0  && SyisSpace((RAW)->zString[0])){\
1025 		(RAW)->nByte--;\
1026 		(RAW)->zString++;\
1027 	}\
1028 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
1029 		(RAW)->nByte--;\
1030 	}
1031 #define SyStringFullTrimSafe(RAW)\
1032 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0  && \
1033           ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
1034 		(RAW)->nByte--;\
1035 		(RAW)->zString++;\
1036 	}\
1037 	while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0  && \
1038                    ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
1039 		(RAW)->nByte--;\
1040 	}
1041 #ifndef JX9_DISABLE_BUILTIN_FUNC
1042 /*
1043  * An XML raw text, CDATA, tag name and son is parsed out and stored
1044  * in an instance of the following structure.
1045  */
1046 typedef struct SyXMLRawStr SyXMLRawStr;
1047 struct SyXMLRawStr
1048 {
1049 	const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */
1050 	sxu32 nByte; /* Text length */
1051 	sxu32 nLine; /* Line number this text occurs */
1052 };
1053 /*
1054  * Event callback signatures.
1055  */
1056 typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr *, SyXMLRawStr *, sxu32, SyXMLRawStr *, void *);
1057 typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *, void *);
1058 typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
1059 typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
1060 typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *, void *);
1061 typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *, int, SyToken *, void *);
1062 typedef sxi32 (*ProcXMLStartDocument)(void *);
1063 typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *, SyXMLRawStr *, void *);
1064 typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *, void *);
1065 typedef sxi32 (*ProcXMLEndDocument)(void *);
1066 /* XML processing control flags */
1067 #define SXML_ENABLE_NAMESPACE	    0x01 /* Parse XML with namespace support enbaled */
1068 #define SXML_ENABLE_QUERY		    0x02 /* Not used */
1069 #define SXML_OPTION_CASE_FOLDING    0x04 /* Controls whether case-folding is enabled for this XML parser */
1070 #define SXML_OPTION_SKIP_TAGSTART   0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/
1071 #define SXML_OPTION_SKIP_WHITE      0x10 /* Whether to skip values consisting of whitespace characters. */
1072 #define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */
1073 /* XML error codes */
1074 enum xml_err_code{
1075     SXML_ERROR_NONE = 1,
1076     SXML_ERROR_NO_MEMORY,
1077     SXML_ERROR_SYNTAX,
1078     SXML_ERROR_NO_ELEMENTS,
1079     SXML_ERROR_INVALID_TOKEN,
1080     SXML_ERROR_UNCLOSED_TOKEN,
1081     SXML_ERROR_PARTIAL_CHAR,
1082     SXML_ERROR_TAG_MISMATCH,
1083     SXML_ERROR_DUPLICATE_ATTRIBUTE,
1084     SXML_ERROR_JUNK_AFTER_DOC_ELEMENT,
1085     SXML_ERROR_PARAM_ENTITY_REF,
1086     SXML_ERROR_UNDEFINED_ENTITY,
1087     SXML_ERROR_RECURSIVE_ENTITY_REF,
1088     SXML_ERROR_ASYNC_ENTITY,
1089     SXML_ERROR_BAD_CHAR_REF,
1090     SXML_ERROR_BINARY_ENTITY_REF,
1091     SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
1092     SXML_ERROR_MISPLACED_XML_PI,
1093     SXML_ERROR_UNKNOWN_ENCODING,
1094     SXML_ERROR_INCORRECT_ENCODING,
1095     SXML_ERROR_UNCLOSED_CDATA_SECTION,
1096     SXML_ERROR_EXTERNAL_ENTITY_HANDLING
1097 };
1098 /* Each active XML SAX parser is represented by an instance
1099  * of the following structure.
1100  */
1101 typedef struct SyXMLParser SyXMLParser;
1102 struct SyXMLParser
1103 {
1104 	SyMemBackend *pAllocator; /* Memory backend */
1105 	void *pUserData;          /* User private data forwarded varbatim by the XML parser
1106 					           * as the last argument to the users callbacks.
1107 						       */
1108 	SyHash hns;               /* Namespace hashtable */
1109 	SySet sToken;             /* XML tokens */
1110 	SyLex sLex;               /* Lexical analyzer */
1111 	sxi32 nFlags;             /* Control flags */
1112 	/* User callbacks */
1113 	ProcXMLStartTagHandler    xStartTag;     /* Start element handler */
1114 	ProcXMLEndTagHandler      xEndTag;       /* End element handler */
1115 	ProcXMLTextHandler        xRaw;          /* Raw text/CDATA handler   */
1116 	ProcXMLDoctypeHandler     xDoctype;      /* DOCTYPE handler */
1117 	ProcXMLPIHandler          xPi;           /* Processing instruction (PI) handler*/
1118 	ProcXMLSyntaxErrorHandler xError;        /* Error handler */
1119 	ProcXMLStartDocument      xStartDoc;     /* StartDoc handler */
1120 	ProcXMLEndDocument        xEndDoc;       /* EndDoc handler */
1121 	ProcXMLNameSpaceStart   xNameSpace;    /* Namespace declaration handler  */
1122 	ProcXMLNameSpaceEnd       xNameSpaceEnd; /* End namespace declaration handler */
1123 };
1124 /*
1125  * --------------
1126  * Archive extractor:
1127  * --------------
1128  * Each open ZIP/TAR archive is identified by an instance of the following structure.
1129  * That is, a process can open one or more archives and manipulates them in thread safe
1130  * way by simply working with pointers to the following structure.
1131  * Each entry in the archive is remembered in a hashtable.
1132  * Lookup is very fast and entry with the same name are chained together.
1133  */
1134  typedef struct SyArchiveEntry SyArchiveEntry;
1135  typedef struct SyArchive SyArchive;
1136  struct SyArchive
1137  {
1138  	SyMemBackend	*pAllocator; /* Memory backend */
1139 	SyArchiveEntry *pCursor;     /* Cursor for linear traversal of archive entries */
1140 	SyArchiveEntry *pList;       /* Pointer to the List of the loaded archive */
1141 	SyArchiveEntry **apHash;     /* Hashtable for archive entry */
1142 	ProcRawStrCmp xCmp;          /* Hash comparison function */
1143 	ProcHash xHash;              /* Hash Function */
1144 	sxu32 nSize;        /* Hashtable size */
1145  	sxu32 nEntry;       /* Total number of entries in the zip/tar archive */
1146  	sxu32 nLoaded;      /* Total number of entries loaded in memory */
1147  	sxu32 nCentralOfft;	/* Central directory offset(ZIP only. Otherwise Zero) */
1148  	sxu32 nCentralSize;	/* Central directory size(ZIP only. Otherwise Zero) */
1149 	void *pUserData;    /* Upper layer private data */
1150 	sxu32 nMagic;       /* Sanity check */
1151 
1152  };
1153 #define SXARCH_MAGIC	0xDEAD635A
1154 #define SXARCH_INVALID(ARCH)            (ARCH == 0  || ARCH->nMagic != SXARCH_MAGIC)
1155 #define SXARCH_ENTRY_INVALID(ENTRY)	    (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC)
1156 #define SyArchiveHashFunc(ARCH)	        (ARCH)->xHash
1157 #define SyArchiveCmpFunc(ARCH)	        (ARCH)->xCmp
1158 #define SyArchiveUserData(ARCH)         (ARCH)->pUserData
1159 #define SyArchiveSetUserData(ARCH, DATA) (ARCH)->pUserData = DATA
1160 /*
1161  * Each loaded archive record is identified by an instance
1162  * of the following structure.
1163  */
1164  struct SyArchiveEntry
1165  {
1166  	sxu32 nByte;         /* Contents size before compression */
1167  	sxu32 nByteCompr;    /* Contents size after compression */
1168 	sxu32 nReadCount;    /* Read counter */
1169  	sxu32 nCrc;          /* Contents CRC32  */
1170  	Sytm  sFmt;	         /* Last-modification time */
1171  	sxu32 nOfft;         /* Data offset. */
1172  	sxu16 nComprMeth;	 /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/
1173  	sxu16 nExtra;        /* Extra size if any */
1174  	SyString sFileName;  /* entry name & length */
1175  	sxu32 nDup;	/* Total number of entries with the same name */
1176 	SyArchiveEntry *pNextHash, *pPrevHash; /* Hash collision chains */
1177  	SyArchiveEntry *pNextName;    /* Next entry with the same name */
1178 	SyArchiveEntry *pNext, *pPrev; /* Next and previous entry in the list */
1179 	sxu32 nHash;     /* Hash of the entry name */
1180  	void *pUserData; /* User data */
1181 	sxu32 nMagic;    /* Sanity check */
1182  };
1183  /*
1184  * Extra flags for extending the file local header
1185  */
1186 #define SXZIP_EXTRA_TIMESTAMP	0x001	/* Extended UNIX timestamp */
1187 #endif /* JX9_DISABLE_BUILTIN_FUNC */
1188 #ifndef JX9_DISABLE_HASH_FUNC
1189 /* MD5 context */
1190 typedef struct MD5Context MD5Context;
1191 struct MD5Context {
1192  sxu32 buf[4];
1193  sxu32 bits[2];
1194  unsigned char in[64];
1195 };
1196 /* SHA1 context */
1197 typedef struct SHA1Context SHA1Context;
1198 struct SHA1Context {
1199   unsigned int state[5];
1200   unsigned int count[2];
1201   unsigned char buffer[64];
1202 };
1203 #endif /* JX9_DISABLE_HASH_FUNC */
1204 /* JX9 private declaration */
1205 /*
1206  * Memory Objects.
1207  * Internally, the JX9 virtual machine manipulates nearly all JX9 values
1208  * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures.
1209  * Each jx9_values struct may cache multiple representations (string, integer etc.)
1210  * of the same value.
1211  */
1212 struct jx9_value
1213 {
1214 	union{
1215 		jx9_real rVal;  /* Real value */
1216 		sxi64 iVal;     /* Integer value */
1217 		void *pOther;   /* Other values (Object, Array, Resource, Namespace, etc.) */
1218 	}x;
1219 	sxi32 iFlags;       /* Control flags (see below) */
1220 	jx9_vm *pVm;        /* VM this instance belong */
1221 	SyBlob sBlob;       /* String values */
1222 	sxu32 nIdx;         /* Object index in the global pool */
1223 };
1224 /* Allowed value types.
1225  */
1226 #define MEMOBJ_STRING    0x001  /* Memory value is a UTF-8 string */
1227 #define MEMOBJ_INT       0x002  /* Memory value is an integer */
1228 #define MEMOBJ_REAL      0x004  /* Memory value is a real number */
1229 #define MEMOBJ_BOOL      0x008  /* Memory value is a boolean */
1230 #define MEMOBJ_NULL      0x020  /* Memory value is NULL */
1231 #define MEMOBJ_HASHMAP   0x040  /* Memory value is a hashmap (JSON representation of Array and Objects)  */
1232 #define MEMOBJ_RES       0x100  /* Memory value is a resource [User private data] */
1233 /* Mask of all known types */
1234 #define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)
1235 /* Scalar variables
1236  * According to the JX9 language reference manual
1237  *  Scalar variables are those containing an integer, float, string or boolean.
1238  *  Types array, object and resource are not scalar.
1239  */
1240 #define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL)
1241 /*
1242  * The following macro clear the current jx9_value type and replace
1243  * it with the given one.
1244  */
1245 #define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE)
1246 /* jx9_value cast method signature */
1247 typedef sxi32 (*ProcMemObjCast)(jx9_value *);
1248 /* Forward reference */
1249 typedef struct jx9_output_consumer jx9_output_consumer;
1250 typedef struct jx9_user_func jx9_user_func;
1251 typedef struct jx9_conf jx9_conf;
1252 /*
1253  * An instance of the following structure store the default VM output
1254  * consumer and it's private data.
1255  * Client-programs can register their own output consumer callback
1256  * via the [JX9_VM_CONFIG_OUTPUT] configuration directive.
1257  * Please refer to the official documentation for more information
1258  * on how to register an output consumer callback.
1259  */
1260 struct jx9_output_consumer
1261 {
1262 	ProcConsumer xConsumer; /* VM output consumer routine */
1263 	void *pUserData;        /* Third argument to xConsumer() */
1264 	ProcConsumer xDef;      /* Default output consumer routine */
1265 	void *pDefData;         /* Third argument to xDef() */
1266 };
1267 /*
1268  * JX9 engine [i.e: jx9 instance] configuration is stored in
1269  * an instance of the following structure.
1270  * Please refer to the official documentation for more information
1271  * on how to configure your jx9 engine instance.
1272  */
1273 struct jx9_conf
1274 {
1275 	ProcConsumer xErr;   /* Compile-time error consumer callback */
1276 	void *pErrData;      /* Third argument to xErr() */
1277 	SyBlob sErrConsumer; /* Default error consumer */
1278 };
1279 /*
1280  * Signature of the C function responsible of expanding constant values.
1281  */
1282 typedef void (*ProcConstant)(jx9_value *, void *);
1283 /*
1284  * Each registered constant [i.e: __TIME__, __DATE__, JX9_OS, INT_MAX, etc.] is stored
1285  * in an instance of the following structure.
1286  * Please refer to the official documentation for more information
1287  * on how to create/install foreign constants.
1288  */
1289 typedef struct jx9_constant jx9_constant;
1290 struct jx9_constant
1291 {
1292 	SyString sName;        /* Constant name */
1293 	ProcConstant xExpand;  /* Function responsible of expanding constant value */
1294 	void *pUserData;       /* Last argument to xExpand() */
1295 };
1296 typedef struct jx9_aux_data jx9_aux_data;
1297 /*
1298  * Auxiliary data associated with each foreign function is stored
1299  * in a stack of the following structure.
1300  * Note that automatic tracked chunks are also stored in an instance
1301  * of this structure.
1302  */
1303 struct jx9_aux_data
1304 {
1305 	void *pAuxData; /* Aux data */
1306 };
1307 /* Foreign functions signature */
1308 typedef int (*ProcHostFunction)(jx9_context *, int, jx9_value **);
1309 /*
1310  * Each installed foreign function is recored in an instance of the following
1311  * structure.
1312  * Please refer to the official documentation for more information on how
1313  * to create/install foreign functions.
1314  */
1315 struct jx9_user_func
1316 {
1317 	jx9_vm *pVm;              /* VM that own this instance */
1318 	SyString sName;           /* Foreign function name */
1319 	ProcHostFunction xFunc;  /* Implementation of the foreign function */
1320 	void *pUserData;          /* User private data [Refer to the official documentation for more information]*/
1321 	SySet aAux;               /* Stack of auxiliary data [Refer to the official documentation for more information]*/
1322 };
1323 /*
1324  * The 'context' argument for an installable function. A pointer to an
1325  * instance of this structure is the first argument to the routines used
1326  * implement the foreign functions.
1327  */
1328 struct jx9_context
1329 {
1330 	jx9_user_func *pFunc;   /* Function information. */
1331 	jx9_value *pRet;        /* Return value is stored here. */
1332 	SySet sVar;             /* Container of dynamically allocated jx9_values
1333 							 * [i.e: Garbage collection purposes.]
1334 							 */
1335 	SySet sChunk;           /* Track dynamically allocated chunks [jx9_aux_data instance].
1336 							 * [i.e: Garbage collection purposes.]
1337 							 */
1338 	jx9_vm *pVm;            /* Virtual machine that own this context */
1339 	sxi32 iFlags;           /* Call flags */
1340 };
1341 /* Hashmap control flags */
1342 #define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/
1343 /*
1344  * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance
1345  * of the following structure.
1346  */
1347 struct jx9_hashmap_node
1348 {
1349 	jx9_hashmap *pMap;     /* Hashmap that own this instance */
1350 	sxi32 iType;           /* Node type */
1351 	union{
1352 		sxi64 iKey;        /* Int key */
1353 		SyBlob sKey;       /* Blob key */
1354 	}xKey;
1355 	sxi32 iFlags;          /* Control flags */
1356 	sxu32 nHash;           /* Key hash value */
1357 	sxu32 nValIdx;         /* Value stored in this node */
1358 	jx9_hashmap_node *pNext, *pPrev;               /* Link to other entries [i.e: linear traversal] */
1359 	jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */
1360 };
1361 /*
1362  * Each active hashmap aka array in the JX9 jargon is represented
1363  * by an instance of the following structure.
1364  */
1365 struct jx9_hashmap
1366 {
1367 	jx9_vm *pVm;                  /* VM that own this instance */
1368 	jx9_hashmap_node **apBucket;  /* Hash bucket */
1369 	jx9_hashmap_node *pFirst;     /* First inserted entry */
1370 	jx9_hashmap_node *pLast;      /* Last inserted entry */
1371 	jx9_hashmap_node *pCur;       /* Current entry */
1372 	sxu32 nSize;                  /* Bucket size */
1373 	sxu32 nEntry;                 /* Total number of inserted entries */
1374 	sxu32 (*xIntHash)(sxi64);     /* Hash function for int_keys */
1375 	sxu32 (*xBlobHash)(const void *, sxu32); /* Hash function for blob_keys */
1376 	sxi32 iFlags;                 /* Hashmap control flags */
1377 	sxi64 iNextIdx;               /* Next available automatically assigned index */
1378 	sxi32 iRef;                   /* Reference count */
1379 };
1380 /* An instance of the following structure is the context
1381  * for the FOREACH_STEP/FOREACH_INIT VM instructions.
1382  * Those instructions are used to implement the 'foreach'
1383  * statement.
1384  * This structure is made available to these instructions
1385  * as the P3 operand.
1386  */
1387 struct jx9_foreach_info
1388 {
1389 	SyString sKey;      /* Key name. Empty otherwise*/
1390 	SyString sValue;    /* Value name */
1391 	sxi32 iFlags;       /* Control flags */
1392 	SySet aStep;        /* Stack of steps [i.e: jx9_foreach_step instance] */
1393 };
1394 struct jx9_foreach_step
1395 {
1396 	sxi32 iFlags;                   /* Control flags (see below) */
1397 	/* Iterate on this map*/
1398 	jx9_hashmap *pMap;          /* Hashmap [i.e: array in the JX9 jargon] iteration
1399 									 * Ex: foreach(array(1, 2, 3) as $key=>$value){}
1400 									 */
1401 
1402 };
1403 /* Foreach step control flags */
1404 #define JX9_4EACH_STEP_KEY     0x001 /* Make Key available */
1405 /*
1406  * Each JX9 engine is identified by an instance of the following structure.
1407  * Please refer to the official documentation for more information
1408  * on how to configure your JX9 engine instance.
1409  */
1410 struct jx9
1411 {
1412 	SyMemBackend sAllocator;     /* Low level memory allocation subsystem */
1413 	const jx9_vfs *pVfs;         /* Underlying Virtual File System */
1414 	jx9_conf xConf;              /* Configuration */
1415 #if defined(JX9_ENABLE_THREADS)
1416 	SyMutex *pMutex;                 /* Per-engine mutex */
1417 #endif
1418 	jx9_vm *pVms;      /* List of active VM */
1419 	sxi32 iVm;         /* Total number of active VM */
1420 	jx9 *pNext, *pPrev; /* List of active engines */
1421 	sxu32 nMagic;      /* Sanity check against misuse */
1422 };
1423 /* Code generation data structures */
1424 typedef sxi32 (*ProcErrorGen)(void *, sxi32, sxu32, const char *, ...);
1425 typedef struct jx9_expr_node   jx9_expr_node;
1426 typedef struct jx9_expr_op     jx9_expr_op;
1427 typedef struct jx9_gen_state   jx9_gen_state;
1428 typedef struct GenBlock        GenBlock;
1429 typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *);
1430 typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32);
1431 /*
1432  * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented
1433  * by an instance of the following structure.
1434  * The JX9 parser does not use any external tools and is 100% handcoded.
1435  * That is, the JX9 parser is thread-safe , full reentrant, produce consistant
1436  * compile-time errrors and at least 7 times faster than the standard JX9 parser.
1437  */
1438 struct jx9_expr_op
1439 {
1440 	SyString sOp;   /* String representation of the operator [i.e: "+", "*", "=="...] */
1441 	sxi32 iOp;      /* Operator ID */
1442 	sxi32 iPrec;    /* Operator precedence: 1 == Highest */
1443 	sxi32 iAssoc;   /* Operator associativity (either left, right or non-associative) */
1444 	sxi32 iVmOp;    /* VM OP code for this operator [i.e: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/
1445 };
1446 /*
1447  * Each expression node is parsed out and recorded
1448  * in an instance of the following structure.
1449  */
1450 struct jx9_expr_node
1451 {
1452 	const jx9_expr_op *pOp;  /* Operator ID or NULL if literal, constant, variable, function or object method call */
1453 	jx9_expr_node *pLeft;    /* Left expression tree */
1454 	jx9_expr_node *pRight;   /* Right expression tree */
1455 	SyToken *pStart;         /* Stream of tokens that belong to this node */
1456 	SyToken *pEnd;           /* End of token stream */
1457 	sxi32 iFlags;            /* Node construct flags */
1458 	ProcNodeConstruct xCode; /* C routine responsible of compiling this node */
1459 	SySet aNodeArgs;         /* Node arguments. Only used by postfix operators [i.e: function call]*/
1460 	jx9_expr_node *pCond;    /* Condition: Only used by the ternary operator '?:' */
1461 };
1462 /* Node Construct flags */
1463 #define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i, --$j] node */
1464 /*
1465  * A block of instructions is recorded in an instance of the following structure.
1466  * This structure is used only during compile-time and have no meaning
1467  * during bytecode execution.
1468  */
1469 struct GenBlock
1470 {
1471 	jx9_gen_state *pGen;  /* State of the code generator */
1472 	GenBlock *pParent;    /* Upper block or NULL if global */
1473 	sxu32 nFirstInstr;    /* First instruction to execute  */
1474 	sxi32 iFlags;         /* Block control flags (see below) */
1475 	SySet aJumpFix;       /* Jump fixup (JumpFixup instance) */
1476 	void *pUserData;      /* Upper layer private data */
1477 	/* The following two fields are used only when compiling
1478 	 * the 'do..while()' language construct.
1479 	 */
1480 	sxu8 bPostContinue;    /* TRUE when compiling the do..while() statement */
1481 	SySet aPostContFix;    /* Post-continue jump fix */
1482 };
1483 /*
1484  * Code generator state is remembered in an instance of the following
1485  * structure. We put the information in this structure and pass around
1486  * a pointer to this structure, rather than pass around  all of the
1487  * information separately. This helps reduce the number of  arguments
1488  * to generator functions.
1489  * This structure is used only during compile-time and have no meaning
1490  * during bytecode execution.
1491  */
1492 struct jx9_gen_state
1493 {
1494 	jx9_vm *pVm;         /* VM that own this instance */
1495 	SyHash hLiteral;     /* Constant string Literals table */
1496 	SyHash hNumLiteral;  /* Numeric literals table */
1497 	SyHash hVar;         /* Collected variable hashtable */
1498 	GenBlock *pCurrent;  /* Current processed block */
1499 	GenBlock sGlobal;    /* Global block */
1500 	ProcConsumer xErr;   /* Error consumer callback */
1501 	void *pErrData;      /* Third argument to xErr() */
1502 	SyToken *pIn;        /* Current processed token */
1503 	SyToken *pEnd;       /* Last token in the stream */
1504 	sxu32 nErr;          /* Total number of compilation error */
1505 };
1506 /* Forward references */
1507 typedef struct jx9_vm_func_static_var  jx9_vm_func_static_var;
1508 typedef struct jx9_vm_func_arg jx9_vm_func_arg;
1509 typedef struct jx9_vm_func jx9_vm_func;
1510 typedef struct VmFrame VmFrame;
1511 /*
1512  * Each collected function argument is recorded in an instance
1513  * of the following structure.
1514  * Note that as an extension, JX9 implements full type hinting
1515  * which mean that any function can have it's own signature.
1516  * Example:
1517  *      function foo(int $a, string $b, float $c, ClassInstance $d){}
1518  * This is how the powerful function overloading mechanism is
1519  * implemented.
1520  * Note that as an extension, JX9 allow function arguments to have
1521  * any complex default value associated with them unlike the standard
1522  * JX9 engine.
1523  * Example:
1524  *    function foo(int $a = rand() & 1023){}
1525  *    now, when foo is called without arguments [i.e: foo()] the
1526  *    $a variable (first parameter) will be set to a random number
1527  *    between 0 and 1023 inclusive.
1528  * Refer to the official documentation for more information on this
1529  * mechanism and other extension introduced by the JX9 engine.
1530  */
1531 struct jx9_vm_func_arg
1532 {
1533 	SyString sName;      /* Argument name */
1534 	SySet aByteCode;     /* Compiled default value associated with this argument */
1535 	sxu32 nType;         /* Type of this argument [i.e: array, int, string, float, object, etc.] */
1536 	sxi32 iFlags;        /* Configuration flags */
1537 };
1538 /*
1539  * Each static variable is parsed out and remembered in an instance
1540  * of the following structure.
1541  * Note that as an extension, JX9 allow static variable have
1542  * any complex default value associated with them unlike the standard
1543  * JX9 engine.
1544  * Example:
1545  *   static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with
1546  *                                         // a random three characters(English alphabet)
1547  *   dump($rand_str);
1548  *   //You should see something like this
1549  *   string(6 'JX9awt');
1550  */
1551 struct jx9_vm_func_static_var
1552 {
1553 	SyString sName;   /* Static variable name */
1554 	SySet aByteCode;  /* Compiled initialization expression  */
1555 	sxu32 nIdx;       /* Object index in the global memory object container */
1556 };
1557 /* Function configuration flags */
1558 #define VM_FUNC_ARG_HAS_DEF  0x001 /* Argument has default value associated with it */
1559 #define VM_FUNC_ARG_IGNORE   0x002 /* Do not install argument in the current frame */
1560 /*
1561  * Each user defined function is parsed out and stored in an instance
1562  * of the following structure.
1563  * JX9 introduced some powerfull extensions to the JX9 5 programming
1564  * language like function overloading, type hinting, complex default
1565  * arguments values and many more.
1566  * Please refer to the official documentation for more information.
1567  */
1568 struct jx9_vm_func
1569 {
1570 	SySet aArgs;         /* Expected arguments (jx9_vm_func_arg instance) */
1571 	SySet aStatic;       /* Static variable (jx9_vm_func_static_var instance) */
1572 	SyString sName;      /* Function name */
1573 	SySet aByteCode;     /* Compiled function body */
1574 	sxi32 iFlags;        /* VM function configuration */
1575 	SyString sSignature; /* Function signature used to implement function overloading
1576 						  * (Refer to the official docuemntation for more information
1577 						  *  on this powerfull feature)
1578 						  */
1579 	void *pUserData;     /* Upper layer private data associated with this instance */
1580 	jx9_vm_func *pNextName; /* Next VM function with the same name as this one */
1581 };
1582 /* Forward reference */
1583 typedef struct jx9_builtin_constant jx9_builtin_constant;
1584 typedef struct jx9_builtin_func jx9_builtin_func;
1585 /*
1586  * Each built-in foreign function (C function) is stored in an
1587  * instance of the following structure.
1588  * Please refer to the official documentation for more information
1589  * on how to create/install foreign functions.
1590  */
1591 struct jx9_builtin_func
1592 {
1593 	const char *zName;        /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/
1594 	ProcHostFunction xFunc;  /* C routine performing the computation */
1595 };
1596 /*
1597  * Each built-in foreign constant is stored in an instance
1598  * of the following structure.
1599  * Please refer to the official documentation for more information
1600  * on how to create/install foreign constants.
1601  */
1602 struct jx9_builtin_constant
1603 {
1604 	const char *zName;     /* Constant name */
1605 	ProcConstant xExpand;  /* C routine responsible of expanding constant value*/
1606 };
1607 /*
1608  * A single instruction of the virtual machine has an opcode
1609  * and as many as three operands.
1610  * Each VM instruction resulting from compiling a JX9 script
1611  * is stored in an instance of the following structure.
1612  */
1613 typedef struct VmInstr VmInstr;
1614 struct VmInstr
1615 {
1616 	sxu8  iOp; /* Operation to preform */
1617 	sxi32 iP1; /* First operand */
1618 	sxu32 iP2; /* Second operand (Often the jump destination) */
1619 	void *p3;  /* Third operand (Often Upper layer private data) */
1620 };
1621 /* Forward reference */
1622 typedef struct jx9_case_expr jx9_case_expr;
1623 typedef struct jx9_switch jx9_switch;
1624 /*
1625  * Each compiled case block in a swicth statement is compiled
1626  * and stored in an instance of the following structure.
1627  */
1628 struct jx9_case_expr
1629 {
1630 	SySet aByteCode;   /* Compiled body of the case block */
1631 	sxu32 nStart;      /* First instruction to execute */
1632 };
1633 /*
1634  * Each compiled switch statement is parsed out and stored
1635  * in an instance of the following structure.
1636  */
1637 struct jx9_switch
1638 {
1639 	SySet aCaseExpr;  /* Compile case block */
1640 	sxu32 nOut;       /* First instruction to execute after this statement */
1641 	sxu32 nDefault;   /* First instruction to execute in the default block */
1642 };
1643 /* Assertion flags */
1644 #define JX9_ASSERT_DISABLE    0x01  /* Disable assertion */
1645 #define JX9_ASSERT_WARNING    0x02  /* Issue a warning for each failed assertion */
1646 #define JX9_ASSERT_BAIL       0x04  /* Terminate execution on failed assertions */
1647 #define JX9_ASSERT_QUIET_EVAL 0x08  /* Not used */
1648 #define JX9_ASSERT_CALLBACK   0x10  /* Callback to call on failed assertions */
1649 /*
1650  * An instance of the following structure hold the bytecode instructions
1651  * resulting from compiling a JX9 script.
1652  * This structure contains the complete state of the virtual machine.
1653  */
1654 struct jx9_vm
1655 {
1656 	SyMemBackend sAllocator;	/* Memory backend */
1657 #if defined(JX9_ENABLE_THREADS)
1658 	SyMutex *pMutex;           /* Recursive mutex associated with this VM. */
1659 #endif
1660 	jx9 *pEngine;               /* Interpreter that own this VM */
1661 	SySet aByteCode;            /* Default bytecode container */
1662 	SySet *pByteContainer;      /* Current bytecode container */
1663 	VmFrame *pFrame;            /* Stack of active frames */
1664 	SyPRNGCtx sPrng;            /* PRNG context */
1665 	SySet aMemObj;              /* Object allocation table */
1666 	SySet aLitObj;              /* Literals allocation table */
1667 	jx9_value *aOps;            /* Operand stack */
1668 	SySet aFreeObj;             /* Stack of free memory objects */
1669 	SyHash hConstant;           /* Host-application and user defined constants container */
1670 	SyHash hHostFunction;       /* Host-application installable functions */
1671 	SyHash hFunction;           /* Compiled functions */
1672 	SyHash hSuper;              /* Global variable */
1673 	SyBlob sConsumer;           /* Default VM consumer [i.e Redirect all VM output to this blob] */
1674 	SyBlob sWorker;             /* General purpose working buffer */
1675 	SyBlob sArgv;               /* $argv[] collector [refer to the [getopt()] implementation for more information] */
1676 	SySet aFiles;               /* Stack of processed files */
1677 	SySet aPaths;               /* Set of import paths */
1678 	SySet aIncluded;            /* Set of included files */
1679 	SySet aIOstream;            /* Installed IO stream container */
1680 	const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */
1681 	jx9_value sExec;           /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/
1682 	void *pStdin;              /* STDIN IO stream */
1683 	void *pStdout;             /* STDOUT IO stream */
1684 	void *pStderr;             /* STDERR IO stream */
1685 	int bErrReport;            /* TRUE to report all runtime Error/Warning/Notice */
1686 	int nRecursionDepth;       /* Current recursion depth */
1687 	int nMaxDepth;             /* Maximum allowed recusion depth */
1688 	sxu32 nOutputLen;          /* Total number of generated output */
1689 	jx9_output_consumer sVmConsumer; /* Registered output consumer callback */
1690 	int iAssertFlags;          /* Assertion flags */
1691 	jx9_value sAssertCallback; /* Callback to call on failed assertions */
1692 	sxi32 iExitStatus;         /* Script exit status */
1693 	jx9_gen_state sCodeGen;    /* Code generator module */
1694 	jx9_vm *pNext, *pPrev;      /* List of active VM's */
1695 	sxu32 nMagic;              /* Sanity check against misuse */
1696 };
1697 /*
1698  * Allowed value for jx9_vm.nMagic
1699  */
1700 #define JX9_VM_INIT   0xEA12CD72  /* VM correctly initialized */
1701 #define JX9_VM_RUN    0xBA851227  /* VM ready to execute JX9 bytecode */
1702 #define JX9_VM_EXEC   0xCDFE1DAD  /* VM executing JX9 bytecode */
1703 #define JX9_VM_STALE  0xDEAD2BAD  /* Stale VM */
1704 /*
1705  * Error codes according to the JX9 language reference manual.
1706  */
1707 enum iErrCode
1708 {
1709     E_ABORT             = -1,  /* deadliness error, should halt script execution. */
1710 	E_ERROR             = 1,   /* Fatal run-time errors. These indicate errors that can not be recovered
1711 							    * from, such as a memory allocation problem. Execution of the script is
1712 							    * halted.
1713 								* The only fatal error under JX9 is an out-of-memory. All others erros
1714 								* even a call to undefined function will not halt script execution.
1715 							    */
1716 	E_WARNING           ,   /* Run-time warnings (non-fatal errors). Execution of the script is not halted.  */
1717 	E_PARSE             ,   /* Compile-time parse errors. Parse errors should only be generated by the parser.*/
1718 	E_NOTICE            ,   /* Run-time notices. Indicate that the script encountered something that could
1719 							    * indicate an error, but could also happen in the normal course of running a script.
1720 							    */
1721 };
1722 /*
1723  * Each VM instruction resulting from compiling a JX9 script is represented
1724  * by one of the following OP codes.
1725  * The program consists of a linear sequence of operations. Each operation
1726  * has an opcode and 3 operands.Operands P1 is an integer.
1727  * Operand P2 is an unsigned integer and operand P3 is a memory address.
1728  * Few opcodes use all 3 operands.
1729  */
1730 enum jx9_vm_op {
1731   JX9_OP_DONE =   1,   /* Done */
1732   JX9_OP_HALT,         /* Halt */
1733   JX9_OP_LOAD,         /* Load memory object */
1734   JX9_OP_LOADC,        /* Load constant */
1735   JX9_OP_LOAD_IDX,     /* Load array entry */
1736   JX9_OP_LOAD_MAP,     /* Load hashmap('array') */
1737   JX9_OP_NOOP,         /* NOOP */
1738   JX9_OP_JMP,          /* Unconditional jump */
1739   JX9_OP_JZ,           /* Jump on zero (FALSE jump) */
1740   JX9_OP_JNZ,          /* Jump on non-zero (TRUE jump) */
1741   JX9_OP_POP,          /* Stack POP */
1742   JX9_OP_CAT,          /* Concatenation */
1743   JX9_OP_CVT_INT,      /* Integer cast */
1744   JX9_OP_CVT_STR,      /* String cast */
1745   JX9_OP_CVT_REAL,     /* Float cast */
1746   JX9_OP_CALL,         /* Function call */
1747   JX9_OP_UMINUS,       /* Unary minus '-'*/
1748   JX9_OP_UPLUS,        /* Unary plus '+'*/
1749   JX9_OP_BITNOT,       /* Bitwise not '~' */
1750   JX9_OP_LNOT,         /* Logical not '!' */
1751   JX9_OP_MUL,          /* Multiplication '*' */
1752   JX9_OP_DIV,          /* Division '/' */
1753   JX9_OP_MOD,          /* Modulus '%' */
1754   JX9_OP_ADD,          /* Add '+' */
1755   JX9_OP_SUB,          /* Sub '-' */
1756   JX9_OP_SHL,          /* Left shift '<<' */
1757   JX9_OP_SHR,          /* Right shift '>>' */
1758   JX9_OP_LT,           /* Less than '<' */
1759   JX9_OP_LE,           /* Less or equal '<=' */
1760   JX9_OP_GT,           /* Greater than '>' */
1761   JX9_OP_GE,           /* Greater or equal '>=' */
1762   JX9_OP_EQ,           /* Equal '==' */
1763   JX9_OP_NEQ,          /* Not equal '!=' */
1764   JX9_OP_TEQ,          /* Type equal '===' */
1765   JX9_OP_TNE,          /* Type not equal '!==' */
1766   JX9_OP_BAND,         /* Bitwise and '&' */
1767   JX9_OP_BXOR,         /* Bitwise xor '^' */
1768   JX9_OP_BOR,          /* Bitwise or '|' */
1769   JX9_OP_LAND,         /* Logical and '&&','and' */
1770   JX9_OP_LOR,          /* Logical or  '||','or' */
1771   JX9_OP_LXOR,         /* Logical xor 'xor' */
1772   JX9_OP_STORE,        /* Store Object */
1773   JX9_OP_STORE_IDX,    /* Store indexed object */
1774   JX9_OP_PULL,         /* Stack pull */
1775   JX9_OP_SWAP,         /* Stack swap */
1776   JX9_OP_YIELD,        /* Stack yield */
1777   JX9_OP_CVT_BOOL,     /* Boolean cast */
1778   JX9_OP_CVT_NUMC,     /* Numeric (integer, real or both) type cast */
1779   JX9_OP_INCR,         /* Increment ++ */
1780   JX9_OP_DECR,         /* Decrement -- */
1781   JX9_OP_ADD_STORE,    /* Add and store '+=' */
1782   JX9_OP_SUB_STORE,    /* Sub and store '-=' */
1783   JX9_OP_MUL_STORE,    /* Mul and store '*=' */
1784   JX9_OP_DIV_STORE,    /* Div and store '/=' */
1785   JX9_OP_MOD_STORE,    /* Mod and store '%=' */
1786   JX9_OP_CAT_STORE,    /* Cat and store '.=' */
1787   JX9_OP_SHL_STORE,    /* Shift left and store '>>=' */
1788   JX9_OP_SHR_STORE,    /* Shift right and store '<<=' */
1789   JX9_OP_BAND_STORE,   /* Bitand and store '&=' */
1790   JX9_OP_BOR_STORE,    /* Bitor and store '|=' */
1791   JX9_OP_BXOR_STORE,   /* Bitxor and store '^=' */
1792   JX9_OP_CONSUME,      /* Consume VM output */
1793   JX9_OP_MEMBER,       /* Object member run-time access */
1794   JX9_OP_UPLINK,       /* Run-Time frame link */
1795   JX9_OP_CVT_NULL,     /* NULL cast */
1796   JX9_OP_CVT_ARRAY,    /* Array cast */
1797   JX9_OP_FOREACH_INIT, /* For each init */
1798   JX9_OP_FOREACH_STEP, /* For each step */
1799   JX9_OP_SWITCH        /* Switch operation */
1800 };
1801 /* -- END-OF INSTRUCTIONS -- */
1802 /*
1803  * Expression Operators ID.
1804  */
1805 enum jx9_expr_id {
1806 	EXPR_OP_DOT,      /* Member access */
1807 	EXPR_OP_DC,        /* :: */
1808 	EXPR_OP_SUBSCRIPT, /* []: Subscripting */
1809 	EXPR_OP_FUNC_CALL, /* func_call() */
1810 	EXPR_OP_INCR,      /* ++ */
1811 	EXPR_OP_DECR,      /* -- */
1812 	EXPR_OP_BITNOT,    /* ~ */
1813 	EXPR_OP_UMINUS,    /* Unary minus  */
1814 	EXPR_OP_UPLUS,     /* Unary plus */
1815 	EXPR_OP_TYPECAST,  /* Type cast [i.e: (int), (float), (string)...] */
1816 	EXPR_OP_ALT,       /* @ */
1817 	EXPR_OP_INSTOF,    /* instanceof */
1818 	EXPR_OP_LOGNOT,    /* logical not ! */
1819 	EXPR_OP_MUL,       /* Multiplication */
1820 	EXPR_OP_DIV,       /* division */
1821 	EXPR_OP_MOD,       /* Modulus */
1822 	EXPR_OP_ADD,       /* Addition */
1823 	EXPR_OP_SUB,       /* Substraction */
1824 	EXPR_OP_DDOT,      /* Concatenation */
1825 	EXPR_OP_SHL,       /* Left shift */
1826 	EXPR_OP_SHR,       /* Right shift */
1827 	EXPR_OP_LT,        /* Less than */
1828 	EXPR_OP_LE,        /* Less equal */
1829 	EXPR_OP_GT,        /* Greater than */
1830 	EXPR_OP_GE,        /* Greater equal */
1831 	EXPR_OP_EQ,        /* Equal == */
1832 	EXPR_OP_NE,        /* Not equal != <> */
1833 	EXPR_OP_TEQ,       /* Type equal === */
1834 	EXPR_OP_TNE,       /* Type not equal !== */
1835 	EXPR_OP_SEQ,       /* String equal 'eq' */
1836 	EXPR_OP_SNE,       /* String not equal 'ne' */
1837 	EXPR_OP_BAND,      /* Biwise and '&' */
1838 	EXPR_OP_REF,       /* Reference operator '&' */
1839 	EXPR_OP_XOR,       /* bitwise xor '^' */
1840 	EXPR_OP_BOR,       /* bitwise or '|' */
1841 	EXPR_OP_LAND,      /* Logical and '&&','and' */
1842 	EXPR_OP_LOR,       /* Logical or  '||','or'*/
1843 	EXPR_OP_LXOR,      /* Logical xor 'xor' */
1844 	EXPR_OP_QUESTY,    /* Ternary operator '?' */
1845 	EXPR_OP_ASSIGN,    /* Assignment '=' */
1846 	EXPR_OP_ADD_ASSIGN, /* Combined operator: += */
1847 	EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */
1848 	EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */
1849 	EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */
1850 	EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */
1851 	EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */
1852 	EXPR_OP_AND_ASSIGN, /* Combined operator: &= */
1853 	EXPR_OP_OR_ASSIGN,  /* Combined operator: |= */
1854 	EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */
1855 	EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */
1856 	EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */
1857 	EXPR_OP_COMMA       /* Comma expression */
1858 };
1859 /*
1860  * Lexer token codes
1861  * The following set of constants are the tokens recognized
1862  * by the lexer when processing JX9 input.
1863  * Important: Token values MUST BE A POWER OF TWO.
1864  */
1865 #define JX9_TK_INTEGER   0x0000001  /* Integer */
1866 #define JX9_TK_REAL      0x0000002  /* Real number */
1867 #define JX9_TK_NUM       (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */
1868 #define JX9_TK_KEYWORD   0x0000004 /* Keyword [i.e: while, for, if, foreach...] */
1869 #define JX9_TK_ID        0x0000008 /* Alphanumeric or UTF-8 stream */
1870 #define JX9_TK_DOLLAR    0x0000010 /* '$' Dollar sign */
1871 #define JX9_TK_OP        0x0000020 /* Operator [i.e: +, *, /...] */
1872 #define JX9_TK_OCB       0x0000040 /* Open curly brace'{' */
1873 #define JX9_TK_CCB       0x0000080 /* Closing curly brace'}' */
1874 #define JX9_TK_DOT       0x0000100 /* Dot . */
1875 #define JX9_TK_LPAREN    0x0000200 /* Left parenthesis '(' */
1876 #define JX9_TK_RPAREN    0x0000400 /* Right parenthesis ')' */
1877 #define JX9_TK_OSB       0x0000800 /* Open square bracket '[' */
1878 #define JX9_TK_CSB       0x0001000 /* Closing square bracket ']' */
1879 #define JX9_TK_DSTR      0x0002000 /* Double quoted string "$str" */
1880 #define JX9_TK_SSTR      0x0004000 /* Single quoted string 'str' */
1881 #define JX9_TK_NOWDOC    0x0010000 /* Nowdoc <<< */
1882 #define JX9_TK_COMMA     0x0020000 /* Comma ',' */
1883 #define JX9_TK_SEMI      0x0040000 /* Semi-colon ";" */
1884 #define JX9_TK_BSTR      0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */
1885 #define JX9_TK_COLON     0x0100000 /* single Colon ':' */
1886 #define JX9_TK_AMPER     0x0200000 /* Ampersand '&' */
1887 #define JX9_TK_EQUAL     0x0400000 /* Equal '=' */
1888 #define JX9_TK_OTHER     0x1000000 /* Other symbols */
1889 /*
1890  * JX9 keyword.
1891  * These words have special meaning in JX9. Some of them represent things which look like
1892  * functions, some look like constants, and so on, but they're not, really: they are language constructs.
1893  * You cannot use any of the following words as constants, object names, function or method names.
1894  * Using them as variable names is generally OK, but could lead to confusion.
1895  */
1896 #define JX9_TKWRD_SWITCH       1 /* switch */
1897 #define JX9_TKWRD_PRINT        2 /* print */
1898 #define JX9_TKWRD_ELIF         0x4000000 /* elseif: MUST BE A POWER OF TWO */
1899 #define JX9_TKWRD_ELSE         0x8000000 /* else:  MUST BE A POWER OF TWO */
1900 #define JX9_TKWRD_IF           3 /* if */
1901 #define JX9_TKWRD_STATIC       4 /* static */
1902 #define JX9_TKWRD_CASE         5 /* case */
1903 #define JX9_TKWRD_FUNCTION     6 /* function */
1904 #define JX9_TKWRD_CONST        7 /* const */
1905 /* The number '8' is reserved for JX9_TK_ID */
1906 #define JX9_TKWRD_WHILE        9 /* while */
1907 #define JX9_TKWRD_DEFAULT      10 /* default */
1908 #define JX9_TKWRD_AS           11 /* as */
1909 #define JX9_TKWRD_CONTINUE     12 /* continue */
1910 #define JX9_TKWRD_EXIT         13 /* exit */
1911 #define JX9_TKWRD_DIE          14 /* die */
1912 #define JX9_TKWRD_IMPORT       15 /* import */
1913 #define JX9_TKWRD_INCLUDE      16 /* include */
1914 #define JX9_TKWRD_FOR          17 /* for */
1915 #define JX9_TKWRD_FOREACH      18 /* foreach */
1916 #define JX9_TKWRD_RETURN       19 /* return */
1917 #define JX9_TKWRD_BREAK        20 /* break */
1918 #define JX9_TKWRD_UPLINK       21 /* uplink */
1919 #define JX9_TKWRD_BOOL         0x8000   /* bool:  MUST BE A POWER OF TWO */
1920 #define JX9_TKWRD_INT          0x10000  /* int:   MUST BE A POWER OF TWO */
1921 #define JX9_TKWRD_FLOAT        0x20000  /* float:  MUST BE A POWER OF TWO */
1922 #define JX9_TKWRD_STRING       0x40000  /* string: MUST BE A POWER OF TWO */
1923 
1924 /* api.c */
1925 JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap);
1926 JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName);
1927 JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName);
1928 /* json.c function prototypes */
1929 JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut);
1930 JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte);
1931 /* memobj.c function prototypes */
1932 JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj);
1933 JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal);
1934 JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore);
1935 JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest);
1936 JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal);
1937 JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray);
1938 #if 0
1939 /* Not used in the current release of the JX9 engine */
1940 JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal);
1941 #endif
1942 JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal);
1943 JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal);
1944 JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj);
1945 JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen);
1946 #if 0
1947 /* Not used in the current release of the JX9 engine */
1948 JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap);
1949 #endif
1950 JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest);
1951 JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest);
1952 JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj);
1953 JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj);
1954 JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj);
1955 JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags);
1956 JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj);
1957 JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj);
1958 JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj);
1959 JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj);
1960 JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj);
1961 JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj);
1962 JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj);
1963 JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj);
1964 JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData);
1965 /* lex.c function prototypes */
1966 JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut);
1967 /* vm.c function prototypes */
1968 JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue);
1969 JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte,
1970 	sxi32 iFlags, void *pUserData);
1971 JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName);
1972 JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData);
1973 JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData);
1974 JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);
1975 JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex);
1976 JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex);
1977 JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString);
1978 JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap);
1979 JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap);
1980 JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage);
1981 JX9_PRIVATE void  jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData);
1982 JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData);
1983 JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine);
1984 JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap);
1985 JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm);
1986 JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar);
1987 JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm);
1988 JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm);
1989 JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm);
1990 JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm);
1991 JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm);
1992 JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm);
1993 JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex);
1994 JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm);
1995 JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer);
1996 JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex);
1997 JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm);
1998 JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult);
1999 JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...);
2000 JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx);
2001 JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen);
2002 JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue);
2003 JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew);
2004 #ifndef JX9_DISABLE_BUILTIN_FUNC
2005 JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte);
2006 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2007 JX9_PRIVATE int jx9Utf8Read(
2008   const unsigned char *z,         /* First byte of UTF-8 character */
2009   const unsigned char *zTerm,     /* Pretend this byte is 0x00 */
2010   const unsigned char **pzNext    /* Write first byte past UTF-8 char here */
2011 );
2012 /* parse.c function prototypes */
2013 JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID);
2014 JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot);
2015 JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext);
2016 JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd);
2017 JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast);
2018 JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet);
2019 /* compile.c function prototypes */
2020 JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType);
2021 JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag);
2022 JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag);
2023 JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag);
2024 JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag);
2025 JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag);
2026 JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag);
2027 JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag);
2028 JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag);
2029 JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
2030 JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
2031 JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...);
2032 JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags);
2033 /* constant.c function prototypes */
2034 JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm);
2035 /* builtin.c function prototypes */
2036 JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm);
2037 /* hashmap.c function prototypes */
2038 JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32));
2039 JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm);
2040 JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS);
2041 JX9_PRIVATE void  jx9HashmapUnref(jx9_hashmap *pMap);
2042 JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode);
2043 JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal);
2044 JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight);
2045 JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest);
2046 JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict);
2047 JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap);
2048 JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap);
2049 JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode);
2050 JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore);
2051 JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey);
2052 JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm);
2053 JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
2054 #ifndef JX9_DISABLE_BUILTIN_FUNC
2055 JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut);
2056 /* builtin.c function prototypes */
2057 JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *),
2058 	jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf);
2059 JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl,
2060 	int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData);
2061 JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData);
2062 JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen);
2063 JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection);
2064 #endif
2065 /* vfs.c */
2066 #ifndef JX9_DISABLE_BUILTIN_FUNC
2067 JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
2068 	int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew);
2069 JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut);
2070 JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle);
2071 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2072 JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen);
2073 JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm);
2074 JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void);
2075 JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm);
2076 JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm);
2077 JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm);
2078 /* lib.c function prototypes */
2079 #ifndef JX9_DISABLE_BUILTIN_FUNC
2080 JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp);
2081 JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch);
2082 JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch);
2083 JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry);
2084 JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen);
2085 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2086 #ifndef JX9_DISABLE_BUILTIN_FUNC
2087 JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData);
2088 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2089 #ifndef JX9_DISABLE_BUILTIN_FUNC
2090 #ifndef JX9_DISABLE_HASH_FUNC
2091 JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen);
2092 JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len);
2093 JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx);
2094 JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx);
2095 JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]);
2096 JX9_PRIVATE void SHA1Init(SHA1Context *context);
2097 JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len);
2098 JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]);
2099 JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]);
2100 #endif
2101 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2102 JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen);
2103 JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData);
2104 JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...);
2105 JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap);
2106 JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...);
2107 JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...);
2108 #ifndef JX9_DISABLE_BUILTIN_FUNC
2109 JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth);
2110 JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay);
2111 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2112 JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8);
2113 #ifndef JX9_DISABLE_BUILTIN_FUNC
2114 JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
2115 #endif
2116 JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex);
2117 JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp);
2118 JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData);
2119 #ifndef JX9_DISABLE_BUILTIN_FUNC
2120 JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
2121 JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
2122 #endif /* JX9_DISABLE_BUILTIN_FUNC */
2123 JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen);
2124 JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2125 JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2126 JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2127 JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2128 JX9_PRIVATE sxi32 SyHexToint(sxi32 c);
2129 JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2130 JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
2131 JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail);
2132 JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData);
2133 JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData);
2134 JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData);
2135 JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen);
2136 JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash);
2137 JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp);
2138 JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx);
2139 JX9_PRIVATE void *SySetPop(SySet *pSet);
2140 JX9_PRIVATE void *SySetPeek(SySet *pSet);
2141 JX9_PRIVATE sxi32 SySetRelease(SySet *pSet);
2142 JX9_PRIVATE sxi32 SySetReset(SySet *pSet);
2143 JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet);
2144 JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry);
2145 JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem);
2146 JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem);
2147 JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize);
2148 #ifndef JX9_DISABLE_BUILTIN_FUNC
2149 JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft);
2150 #endif
2151 JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob);
2152 JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob);
2153 JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen);
2154 JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest);
2155 JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob);
2156 JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize);
2157 JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte);
2158 JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator);
2159 JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize);
2160 JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize);
2161 JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize);
2162 JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend);
2163 JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData);
2164 JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData);
2165 JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent);
2166 #if 0
2167 /* Not used in the current release of the JX9 engine */
2168 JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
2169 #endif
2170 JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk);
2171 JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte);
2172 JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
2173 JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
2174 JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte);
2175 JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
2176 JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
2177 JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
2178 JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen);
2179 JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen);
2180 #if !defined(JX9_DISABLE_BUILTIN_FUNC) || defined(__APPLE__)
2181 JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen);
2182 #endif
2183 JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos);
2184 #ifndef JX9_DISABLE_BUILTIN_FUNC
2185 JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
2186 #endif
2187 JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
2188 JX9_PRIVATE sxu32 SyStrlen(const char *zSrc);
2189 #if defined(JX9_ENABLE_THREADS)
2190 JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
2191 JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
2192 JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
2193 #endif
2194 JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb);
2195 JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB);
2196 JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb);
2197 JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB);
2198 JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64);
2199 JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64);
2200 JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64);
2201 JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32);
2202 JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16);
2203 JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut);
2204 JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut);
2205 #endif /* __JX9INT_H__ */
2206 
2207 /* unqlite.h */
2208 /* This file was automatically generated.  Do not edit (Except for compile time directives)! */
2209 #ifndef _UNQLITE_H_
2210 #define _UNQLITE_H_
2211 /*
2212  * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
2213  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
2214  * Version 1.1.6
2215  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
2216  * please contact Symisc Systems via:
2217  *       legal@symisc.net
2218  *       licensing@symisc.net
2219  *       contact@symisc.net
2220  * or visit:
2221  *      http://unqlite.org/licensing.html
2222  */
2223 /*
2224  * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
2225  * All rights reserved.
2226  *
2227  * Redistribution and use in source and binary forms, with or without
2228  * modification, are permitted provided that the following conditions
2229  * are met:
2230  * 1. Redistributions of source code must retain the above copyright
2231  *    notice, this list of conditions and the following disclaimer.
2232  * 2. Redistributions in binary form must reproduce the above copyright
2233  *    notice, this list of conditions and the following disclaimer in the
2234  *    documentation and/or other materials provided with the distribution.
2235  *
2236  * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
2237  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2238  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
2239  * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
2240  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2241  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2242  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2243  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2244  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
2245  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2246  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2247  */
2248  /* $SymiscID: unqlite.h v1.1 UNIX|WIN32/64 2012-11-02 02:10 stable <chm@symisc.net> $ */
2249 #include <stdarg.h> /* needed for the definition of va_list */
2250 /*
2251  * Compile time engine version, signature, identification in the symisc source tree
2252  * and copyright notice.
2253  * Each macro have an equivalent C interface associated with it that provide the same
2254  * information but are associated with the library instead of the header file.
2255  * Refer to [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
2256  * [unqlite_lib_copyright()] for more information.
2257  */
2258 /*
2259  * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
2260  * that is the unqlite version in the format "X.Y.Z" where X is the major
2261  * version number and Y is the minor version number and Z is the release
2262  * number.
2263  */
2264 #define UNQLITE_VERSION "1.1.6"
2265 /*
2266  * The UNQLITE_VERSION_NUMBER C preprocessor macro resolves to an integer
2267  * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
2268  * numbers used in [UNQLITE_VERSION].
2269  */
2270 #define UNQLITE_VERSION_NUMBER 1001006
2271 /*
2272  * The UNQLITE_SIG C preprocessor macro evaluates to a string
2273  * literal which is the public signature of the unqlite engine.
2274  * This signature could be included for example in a host-application
2275  * generated Server MIME header as follows:
2276  *   Server: YourWebServer/x.x unqlite/x.x.x \r\n
2277  */
2278 #define UNQLITE_SIG "unqlite/1.1.6"
2279 /*
2280  * UnQLite identification in the Symisc source tree:
2281  * Each particular check-in of a particular software released
2282  * by symisc systems have an unique identifier associated with it.
2283  * This macro hold the one associated with unqlite.
2284  */
2285 #define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
2286 /*
2287  * Copyright notice.
2288  * If you have any questions about the licensing situation, please
2289  * visit http://unqlite.org/licensing.html
2290  * or contact Symisc Systems via:
2291  *   legal@symisc.net
2292  *   licensing@symisc.net
2293  *   contact@symisc.net
2294  */
2295 #define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2013, http://unqlite.org/"
2296 /* Make sure we can call this stuff from C++ */
2297 #ifdef __cplusplus
2298 extern "C" {
2299 #endif
2300 /* Forward declaration to public objects */
2301 typedef struct unqlite_io_methods unqlite_io_methods;
2302 typedef struct unqlite_kv_methods unqlite_kv_methods;
2303 typedef struct unqlite_kv_engine unqlite_kv_engine;
2304 typedef struct jx9_io_stream unqlite_io_stream;
2305 typedef struct jx9_context unqlite_context;
2306 typedef struct jx9_value unqlite_value;
2307 typedef struct unqlite_vfs unqlite_vfs;
2308 typedef struct unqlite_vm unqlite_vm;
2309 typedef struct unqlite unqlite;
2310 /*
2311  * ------------------------------
2312  * Compile time directives
2313  * ------------------------------
2314  * For most purposes, UnQLite can be built just fine using the default compilation options.
2315  * However, if required, the compile-time options documented below can be used to omit UnQLite
2316  * features (resulting in a smaller compiled library size) or to change the default values
2317  * of some parameters.
2318  * Every effort has been made to ensure that the various combinations of compilation options
2319  * work harmoniously and produce a working library.
2320  *
2321  * UNQLITE_ENABLE_THREADS
2322  *  This option controls whether or not code is included in UnQLite to enable it to operate
2323  *  safely in a multithreaded environment. The default is not. All mutexing code is omitted
2324  *  and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
2325  *  UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
2326  *  and it is safe to share the same virtual machine and engine handle between two or more threads.
2327  *  The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
2328  *  interface.
2329  *  When UnQLite has been compiled with threading support then the threading mode can be altered
2330  * at run-time using the unqlite_lib_config() interface together with one of these verbs:
2331  *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
2332  *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
2333  *  Platforms others than Windows and UNIX systems must install their own mutex subsystem via
2334  *  unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
2335  *  Otherwise the library is not threadsafe.
2336  *  Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
2337  *
2338  * Options To Omit/Enable Features
2339  *
2340  * The following options can be used to reduce the size of the compiled library by omitting optional
2341  * features. This is probably only useful in embedded systems where space is especially tight, as even
2342  * with all features included the UnQLite library is relatively small. Don't forget to tell your
2343  * compiler to optimize for binary size! (the -Os option if using GCC). Telling your compiler
2344  * to optimize for size usually has a much larger impact on library footprint than employing
2345  * any of these compile-time options.
2346  *
2347  * JX9_DISABLE_BUILTIN_FUNC
2348  *  Jx9 is shipped with more than 312 built-in functions suitable for most purposes like
2349  *  string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
2350  *  and so forth.
2351  *  If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
2352  *  Note that special functions such as db_create(), db_store(), db_fetch(), etc. are not omitted
2353  *  from the build and are not affected by this directive.
2354  *
2355  * JX9_ENABLE_MATH_FUNC
2356  *  If this directive is enabled, built-in math functions such as sqrt(), abs(), log(), ceil(), etc.
2357  *  are included in the build. Note that you may need to link UnQLite with the math library in same
2358  *  Linux/BSD flavor (i.e: -lm).
2359  *
2360  * JX9_DISABLE_DISK_IO
2361  *  If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
2362  *  sleep(), etc. are omitted from the build.
2363  *
2364  * UNQLITE_ENABLE_JX9_HASH_IO
2365  * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
2366  * are included in the build.
2367  */
2368 /* Symisc public definitions */
2369 #if !defined(SYMISC_STANDARD_DEFS)
2370 #define SYMISC_STANDARD_DEFS
2371 #if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE)
2372 /* Windows Systems */
2373 #if !defined(__WINNT__)
2374 #define __WINNT__
2375 #endif
2376 /*
2377  * Determine if we are dealing with WindowsCE - which has a much
2378  * reduced API.
2379  */
2380 #if defined(_WIN32_WCE)
2381 #ifndef __WIN_CE__
2382 #define __WIN_CE__
2383 #endif /* __WIN_CE__ */
2384 #endif /* _WIN32_WCE */
2385 #else
2386 /*
2387  * By default we will assume that we are compiling on a UNIX systems.
2388  * Otherwise the OS_OTHER directive must be defined.
2389  */
2390 #if !defined(OS_OTHER)
2391 #if !defined(__UNIXES__)
2392 #define __UNIXES__
2393 #endif /* __UNIXES__ */
2394 #else
2395 #endif /* OS_OTHER */
2396 #endif /* __WINNT__/__UNIXES__ */
2397 #if defined(_MSC_VER) || defined(__BORLANDC__)
2398 typedef signed __int64     sxi64; /* 64 bits(8 bytes) signed int64 */
2399 typedef unsigned __int64   sxu64; /* 64 bits(8 bytes) unsigned int64 */
2400 #else
2401 typedef signed long long int   sxi64; /* 64 bits(8 bytes) signed int64 */
2402 typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */
2403 #endif /* _MSC_VER */
2404 /* Signature of the consumer routine */
2405 typedef int (*ProcConsumer)(const void *, unsigned int, void *);
2406 /* Forward reference */
2407 typedef struct SyMutexMethods SyMutexMethods;
2408 typedef struct SyMemMethods SyMemMethods;
2409 typedef struct SyString SyString;
2410 typedef struct syiovec syiovec;
2411 typedef struct SyMutex SyMutex;
2412 typedef struct Sytm Sytm;
2413 /* Scatter and gather array. */
2414 struct syiovec
2415 {
2416 #if defined (__WINNT__)
2417 	/* Same fields type and offset as WSABUF structure defined one winsock2 header */
2418 	unsigned long nLen;
2419 	char *pBase;
2420 #else
2421 	void *pBase;
2422 	unsigned long nLen;
2423 #endif
2424 };
2425 struct SyString
2426 {
2427 	const char *zString;  /* Raw string (may not be null terminated) */
2428 	unsigned int nByte;   /* Raw string length */
2429 };
2430 /* Time structure. */
2431 struct Sytm
2432 {
2433   int tm_sec;     /* seconds (0 - 60) */
2434   int tm_min;     /* minutes (0 - 59) */
2435   int tm_hour;    /* hours (0 - 23) */
2436   int tm_mday;    /* day of month (1 - 31) */
2437   int tm_mon;     /* month of year (0 - 11) */
2438   int tm_year;    /* year + 1900 */
2439   int tm_wday;    /* day of week (Sunday = 0) */
2440   int tm_yday;    /* day of year (0 - 365) */
2441   int tm_isdst;   /* is summer time in effect? */
2442   char *tm_zone;  /* abbreviation of timezone name */
2443   long tm_gmtoff; /* offset from UTC in seconds */
2444 };
2445 /* Convert a tm structure (struct tm *) found in <time.h> to a Sytm structure */
2446 #define STRUCT_TM_TO_SYTM(pTM, pSYTM) \
2447 	(pSYTM)->tm_hour = (pTM)->tm_hour;\
2448 	(pSYTM)->tm_min	 = (pTM)->tm_min;\
2449 	(pSYTM)->tm_sec	 = (pTM)->tm_sec;\
2450 	(pSYTM)->tm_mon	 = (pTM)->tm_mon;\
2451 	(pSYTM)->tm_mday = (pTM)->tm_mday;\
2452 	(pSYTM)->tm_year = (pTM)->tm_year + 1900;\
2453 	(pSYTM)->tm_yday = (pTM)->tm_yday;\
2454 	(pSYTM)->tm_wday = (pTM)->tm_wday;\
2455 	(pSYTM)->tm_isdst = (pTM)->tm_isdst;\
2456 	(pSYTM)->tm_gmtoff = 0;\
2457 	(pSYTM)->tm_zone = 0;
2458 
2459 /* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */
2460 #define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \
2461 	 (pSYTM)->tm_hour = (pSYSTIME)->wHour;\
2462 	 (pSYTM)->tm_min  = (pSYSTIME)->wMinute;\
2463 	 (pSYTM)->tm_sec  = (pSYSTIME)->wSecond;\
2464 	 (pSYTM)->tm_mon  = (pSYSTIME)->wMonth - 1;\
2465 	 (pSYTM)->tm_mday = (pSYSTIME)->wDay;\
2466 	 (pSYTM)->tm_year = (pSYSTIME)->wYear;\
2467 	 (pSYTM)->tm_yday = 0;\
2468 	 (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\
2469 	 (pSYTM)->tm_gmtoff = 0;\
2470 	 (pSYTM)->tm_isdst = -1;\
2471 	 (pSYTM)->tm_zone = 0;
2472 
2473 /* Dynamic memory allocation methods. */
2474 struct SyMemMethods
2475 {
2476 	void * (*xAlloc)(unsigned int);          /* [Required:] Allocate a memory chunk */
2477 	void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */
2478 	void   (*xFree)(void *);                 /* [Required:] Release a memory chunk */
2479 	unsigned int  (*xChunkSize)(void *);     /* [Optional:] Return chunk size */
2480 	int    (*xInit)(void *);                 /* [Optional:] Initialization callback */
2481 	void   (*xRelease)(void *);              /* [Optional:] Release callback */
2482 	void  *pUserData;                        /* [Optional:] First argument to xInit() and xRelease() */
2483 };
2484 /* Out of memory callback signature. */
2485 typedef int (*ProcMemError)(void *);
2486 /* Mutex methods. */
2487 struct SyMutexMethods
2488 {
2489 	int (*xGlobalInit)(void);		/* [Optional:] Global mutex initialization */
2490 	void  (*xGlobalRelease)(void);	/* [Optional:] Global Release callback () */
2491 	SyMutex * (*xNew)(int);	        /* [Required:] Request a new mutex */
2492 	void  (*xRelease)(SyMutex *);	/* [Optional:] Release a mutex  */
2493 	void  (*xEnter)(SyMutex *);	    /* [Required:] Enter mutex */
2494 	int (*xTryEnter)(SyMutex *);    /* [Optional:] Try to enter a mutex */
2495 	void  (*xLeave)(SyMutex *);	    /* [Required:] Leave a locked mutex */
2496 };
2497 #if defined (_MSC_VER) || defined (__MINGW32__) ||  defined (__GNUC__) && defined (__declspec)
2498 #define SX_APIIMPORT	__declspec(dllimport)
2499 #define SX_APIEXPORT	__declspec(dllexport)
2500 #else
2501 #define	SX_APIIMPORT
2502 #define	SX_APIEXPORT
2503 #endif
2504 /* Standard return values from Symisc public interfaces */
2505 #define SXRET_OK       0      /* Not an error */
2506 #define SXERR_MEM      (-1)   /* Out of memory */
2507 #define SXERR_IO       (-2)   /* IO error */
2508 #define SXERR_EMPTY    (-3)   /* Empty field */
2509 #define SXERR_LOCKED   (-4)   /* Locked operation */
2510 #define SXERR_ORANGE   (-5)   /* Out of range value */
2511 #define SXERR_NOTFOUND (-6)   /* Item not found */
2512 #define SXERR_LIMIT    (-7)   /* Limit reached */
2513 #define SXERR_MORE     (-8)   /* Need more input */
2514 #define SXERR_INVALID  (-9)   /* Invalid parameter */
2515 #define SXERR_ABORT    (-10)  /* User callback request an operation abort */
2516 #define SXERR_EXISTS   (-11)  /* Item exists */
2517 #define SXERR_SYNTAX   (-12)  /* Syntax error */
2518 #define SXERR_UNKNOWN  (-13)  /* Unknown error */
2519 #define SXERR_BUSY     (-14)  /* Busy operation */
2520 #define SXERR_OVERFLOW (-15)  /* Stack or buffer overflow */
2521 #define SXERR_WILLBLOCK (-16) /* Operation will block */
2522 #define SXERR_NOTIMPLEMENTED  (-17) /* Operation not implemented */
2523 #define SXERR_EOF      (-18) /* End of input */
2524 #define SXERR_PERM     (-19) /* Permission error */
2525 #define SXERR_NOOP     (-20) /* No-op */
2526 #define SXERR_FORMAT   (-21) /* Invalid format */
2527 #define SXERR_NEXT     (-22) /* Not an error */
2528 #define SXERR_OS       (-23) /* System call return an error */
2529 #define SXERR_CORRUPT  (-24) /* Corrupted pointer */
2530 #define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */
2531 #define SXERR_NOMATCH  (-26) /* No match */
2532 #define SXERR_RESET    (-27) /* Operation reset */
2533 #define SXERR_DONE     (-28) /* Not an error */
2534 #define SXERR_SHORT    (-29) /* Buffer too short */
2535 #define SXERR_PATH     (-30) /* Path error */
2536 #define SXERR_TIMEOUT  (-31) /* Timeout */
2537 #define SXERR_BIG      (-32) /* Too big for processing */
2538 #define SXERR_RETRY    (-33) /* Retry your call */
2539 #define SXERR_IGNORE   (-63) /* Ignore */
2540 #endif /* SYMISC_PUBLIC_DEFS */
2541 /*
2542  * Marker for exported interfaces.
2543  */
2544 #define UNQLITE_APIEXPORT SX_APIEXPORT
2545 /*
2546  * If compiling for a processor that lacks floating point
2547  * support, substitute integer for floating-point.
2548  */
2549 #ifdef UNQLITE_OMIT_FLOATING_POINT
2550 typedef sxi64 uqlite_real;
2551 #else
2552 typedef double unqlite_real;
2553 #endif
2554 typedef sxi64 unqlite_int64;
2555 /* Standard UnQLite return values */
2556 #define UNQLITE_OK      SXRET_OK      /* Successful result */
2557 /* Beginning of error codes */
2558 #define UNQLITE_NOMEM    SXERR_MEM     /* Out of memory */
2559 #define UNQLITE_ABORT    SXERR_ABORT   /* Another thread have released this instance */
2560 #define UNQLITE_IOERR    SXERR_IO      /* IO error */
2561 #define UNQLITE_CORRUPT  SXERR_CORRUPT /* Corrupt pointer */
2562 #define UNQLITE_LOCKED   SXERR_LOCKED  /* Forbidden Operation */
2563 #define UNQLITE_BUSY	 SXERR_BUSY    /* The database file is locked */
2564 #define UNQLITE_DONE	 SXERR_DONE    /* Operation done */
2565 #define UNQLITE_PERM     SXERR_PERM    /* Permission error */
2566 #define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
2567 #define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
2568 #define UNQLITE_NOOP     SXERR_NOOP     /* No such method */
2569 #define UNQLITE_INVALID  SXERR_INVALID  /* Invalid parameter */
2570 #define UNQLITE_EOF      SXERR_EOF      /* End Of Input */
2571 #define UNQLITE_UNKNOWN  SXERR_UNKNOWN  /* Unknown configuration option */
2572 #define UNQLITE_LIMIT    SXERR_LIMIT    /* Database limit reached */
2573 #define UNQLITE_EXISTS   SXERR_EXISTS   /* Record exists */
2574 #define UNQLITE_EMPTY    SXERR_EMPTY    /* Empty record */
2575 #define UNQLITE_COMPILE_ERR (-70)       /* Compilation error */
2576 #define UNQLITE_VM_ERR      (-71)       /* Virtual machine error */
2577 #define UNQLITE_FULL        (-73)       /* Full database (unlikely) */
2578 #define UNQLITE_CANTOPEN    (-74)       /* Unable to open the database file */
2579 #define UNQLITE_READ_ONLY   (-75)       /* Read only Key/Value storage engine */
2580 #define UNQLITE_LOCKERR     (-76)       /* Locking protocol error */
2581 /* end-of-error-codes */
2582 /*
2583  * Database Handle Configuration Commands.
2584  *
2585  * The following set of constants are the available configuration verbs that can
2586  * be used by the host-application to configure an UnQLite database handle.
2587  * These constants must be passed as the second argument to [unqlite_config()].
2588  *
2589  * Each options require a variable number of arguments.
2590  * The [unqlite_config()] interface will return UNQLITE_OK on success, any other
2591  * return value indicates failure.
2592  * For a full discussion on the configuration verbs and their expected
2593  * parameters, please refer to this page:
2594  *      http://unqlite.org/c_api/unqlite_config.html
2595  */
2596 #define UNQLITE_CONFIG_JX9_ERR_LOG         1  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
2597 #define UNQLITE_CONFIG_MAX_PAGE_CACHE      2  /* ONE ARGUMENT: int nMaxPage */
2598 #define UNQLITE_CONFIG_ERR_LOG             3  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
2599 #define UNQLITE_CONFIG_KV_ENGINE           4  /* ONE ARGUMENT: const char *zKvName */
2600 #define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5  /* NO ARGUMENTS */
2601 #define UNQLITE_CONFIG_GET_KV_NAME         6  /* ONE ARGUMENT: const char **pzPtr */
2602 /*
2603  * UnQLite/Jx9 Virtual Machine Configuration Commands.
2604  *
2605  * The following set of constants are the available configuration verbs that can
2606  * be used by the host-application to configure the Jx9 (Via UnQLite) Virtual machine.
2607  * These constants must be passed as the second argument to the [unqlite_vm_config()]
2608  * interface.
2609  * Each options require a variable number of arguments.
2610  * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
2611  * value indicates failure.
2612  * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
2613  * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
2614  * a HTTP request and UNQLITE_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
2615  * For a full discussion on the configuration verbs and their expected parameters, please
2616  * refer to this page:
2617  *      http://unqlite.org/c_api/unqlite_vm_config.html
2618  */
2619 #define UNQLITE_VM_CONFIG_OUTPUT           1  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
2620 #define UNQLITE_VM_CONFIG_IMPORT_PATH      2  /* ONE ARGUMENT: const char *zIncludePath */
2621 #define UNQLITE_VM_CONFIG_ERR_REPORT       3  /* NO ARGUMENTS: Report all run-time errors in the VM output */
2622 #define UNQLITE_VM_CONFIG_RECURSION_DEPTH  4  /* ONE ARGUMENT: int nMaxDepth */
2623 #define UNQLITE_VM_OUTPUT_LENGTH           5  /* ONE ARGUMENT: unsigned int *pLength */
2624 #define UNQLITE_VM_CONFIG_CREATE_VAR       6  /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
2625 #define UNQLITE_VM_CONFIG_HTTP_REQUEST     7  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
2626 #define UNQLITE_VM_CONFIG_SERVER_ATTR      8  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
2627 #define UNQLITE_VM_CONFIG_ENV_ATTR         9  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
2628 #define UNQLITE_VM_CONFIG_EXEC_VALUE      10  /* ONE ARGUMENT: unqlite_value **ppValue */
2629 #define UNQLITE_VM_CONFIG_IO_STREAM       11  /* ONE ARGUMENT: const unqlite_io_stream *pStream */
2630 #define UNQLITE_VM_CONFIG_ARGV_ENTRY      12  /* ONE ARGUMENT: const char *zValue */
2631 #define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  13  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
2632 /*
2633  * Storage engine configuration commands.
2634  *
2635  * The following set of constants are the available configuration verbs that can
2636  * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
2637  * These constants must be passed as the first argument to [unqlite_kv_config()].
2638  * Each options require a variable number of arguments.
2639  * The [unqlite_kv_config()] interface will return UNQLITE_OK on success, any other return
2640  * value indicates failure.
2641  * For a full discussion on the configuration verbs and their expected parameters, please
2642  * refer to this page:
2643  *      http://unqlite.org/c_api/unqlite_kv_config.html
2644  */
2645 #define UNQLITE_KV_CONFIG_HASH_FUNC  1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
2646 #define UNQLITE_KV_CONFIG_CMP_FUNC   2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
2647 /*
2648  * Global Library Configuration Commands.
2649  *
2650  * The following set of constants are the available configuration verbs that can
2651  * be used by the host-application to configure the whole library.
2652  * These constants must be passed as the first argument to [unqlite_lib_config()].
2653  *
2654  * Each options require a variable number of arguments.
2655  * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
2656  * value indicates failure.
2657  * Notes:
2658  * The default configuration is recommended for most applications and so the call to
2659  * [unqlite_lib_config()] is usually not necessary. It is provided to support rare
2660  * applications with unusual needs.
2661  * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
2662  * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
2663  * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
2664  * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
2665  * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
2666  * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
2667  * For a full discussion on the configuration verbs and their expected parameters, please
2668  * refer to this page:
2669  *      http://unqlite.org/c_api/unqlite_lib.html
2670  */
2671 #define UNQLITE_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
2672 #define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
2673 #define UNQLITE_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
2674 #define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */
2675 #define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */
2676 #define UNQLITE_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
2677 #define UNQLITE_LIB_CONFIG_STORAGE_ENGINE         7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
2678 #define UNQLITE_LIB_CONFIG_PAGE_SIZE              8 /* ONE ARGUMENT: int iPageSize */
2679 /*
2680  * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
2681  * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
2682  */
2683 #define UNQLITE_OPEN_READONLY         0x00000001  /* Read only mode. Ok for [unqlite_open] */
2684 #define UNQLITE_OPEN_READWRITE        0x00000002  /* Ok for [unqlite_open] */
2685 #define UNQLITE_OPEN_CREATE           0x00000004  /* Ok for [unqlite_open] */
2686 #define UNQLITE_OPEN_EXCLUSIVE        0x00000008  /* VFS only */
2687 #define UNQLITE_OPEN_TEMP_DB          0x00000010  /* VFS only */
2688 #define UNQLITE_OPEN_NOMUTEX          0x00000020  /* Ok for [unqlite_open] */
2689 #define UNQLITE_OPEN_OMIT_JOURNALING  0x00000040  /* Omit journaling for this database. Ok for [unqlite_open] */
2690 #define UNQLITE_OPEN_IN_MEMORY        0x00000080  /* An in memory database. Ok for [unqlite_open]*/
2691 #define UNQLITE_OPEN_MMAP             0x00000100  /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
2692 /*
2693  * Synchronization Type Flags
2694  *
2695  * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
2696  * a combination of these integer values as the second argument.
2697  *
2698  * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
2699  * needs to flush data to mass storage.  Inode information need not be flushed.
2700  * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
2701  * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
2702  * Mac OS X style fullsync instead of fsync().
2703  */
2704 #define UNQLITE_SYNC_NORMAL        0x00002
2705 #define UNQLITE_SYNC_FULL          0x00003
2706 #define UNQLITE_SYNC_DATAONLY      0x00010
2707 /*
2708  * File Locking Levels
2709  *
2710  * UnQLite uses one of these integer values as the second
2711  * argument to calls it makes to the xLock() and xUnlock() methods
2712  * of an [unqlite_io_methods] object.
2713  */
2714 #define UNQLITE_LOCK_NONE          0
2715 #define UNQLITE_LOCK_SHARED        1
2716 #define UNQLITE_LOCK_RESERVED      2
2717 #define UNQLITE_LOCK_PENDING       3
2718 #define UNQLITE_LOCK_EXCLUSIVE     4
2719 /*
2720  * CAPIREF: OS Interface: Open File Handle
2721  *
2722  * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
2723  * layer.
2724  * Individual OS interface implementations will want to subclass this object by appending
2725  * additional fields for their own use. The pMethods entry is a pointer to an
2726  * [unqlite_io_methods] object that defines methods for performing
2727  * I/O operations on the open file.
2728 */
2729 typedef struct unqlite_file unqlite_file;
2730 struct unqlite_file {
2731   const unqlite_io_methods *pMethods;  /* Methods for an open file. MUST BE FIRST */
2732 };
2733 /*
2734  * CAPIREF: OS Interface: File Methods Object
2735  *
2736  * Every file opened by the [unqlite_vfs] xOpen method populates an
2737  * [unqlite_file] object (or, more commonly, a subclass of the
2738  * [unqlite_file] object) with a pointer to an instance of this object.
2739  * This object defines the methods used to perform various operations
2740  * against the open file represented by the [unqlite_file] object.
2741  *
2742  * If the xOpen method sets the unqlite_file.pMethods element
2743  * to a non-NULL pointer, then the unqlite_io_methods.xClose method
2744  * may be invoked even if the xOpen reported that it failed.  The
2745  * only way to prevent a call to xClose following a failed xOpen
2746  * is for the xOpen to set the unqlite_file.pMethods element to NULL.
2747  *
2748  * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
2749  * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
2750  * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
2751  * flag may be ORed in to indicate that only the data of the file
2752  * and not its inode needs to be synced.
2753  *
2754  * The integer values to xLock() and xUnlock() are one of
2755  *
2756  * UNQLITE_LOCK_NONE
2757  * UNQLITE_LOCK_SHARED
2758  * UNQLITE_LOCK_RESERVED
2759  * UNQLITE_LOCK_PENDING
2760  * UNQLITE_LOCK_EXCLUSIVE
2761  *
2762  * xLock() increases the lock. xUnlock() decreases the lock.
2763  * The xCheckReservedLock() method checks whether any database connection,
2764  * either in this process or in some other process, is holding a RESERVED,
2765  * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
2766  * and false otherwise.
2767  *
2768  * The xSectorSize() method returns the sector size of the device that underlies
2769  * the file. The sector size is the minimum write that can be performed without
2770  * disturbing other bytes in the file.
2771  *
2772  */
2773 struct unqlite_io_methods {
2774   int iVersion;                 /* Structure version number (currently 1) */
2775   int (*xClose)(unqlite_file*);
2776   int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
2777   int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
2778   int (*xTruncate)(unqlite_file*, unqlite_int64 size);
2779   int (*xSync)(unqlite_file*, int flags);
2780   int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
2781   int (*xLock)(unqlite_file*, int);
2782   int (*xUnlock)(unqlite_file*, int);
2783   int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
2784   int (*xSectorSize)(unqlite_file*);
2785 };
2786 /*
2787  * CAPIREF: OS Interface Object
2788  *
2789  * An instance of the unqlite_vfs object defines the interface between
2790  * the UnQLite core and the underlying operating system.  The "vfs"
2791  * in the name of the object stands for "Virtual File System".
2792  *
2793  * Only a single vfs can be registered within the UnQLite core.
2794  * Vfs registration is done using the [unqlite_lib_config()] interface
2795  * with a configuration verb set to UNQLITE_LIB_CONFIG_VFS.
2796  * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users
2797  * does not have to worry about registering and installing a vfs since UnQLite
2798  * come with a built-in vfs for these platforms that implements most the methods
2799  * defined below.
2800  *
2801  * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
2802  * must register their own vfs in order to be able to use the UnQLite library.
2803  *
2804  * The value of the iVersion field is initially 1 but may be larger in
2805  * future versions of UnQLite.
2806  *
2807  * The szOsFile field is the size of the subclassed [unqlite_file] structure
2808  * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
2809  *
2810  * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
2811  * structure passed as the third argument to xOpen. The xOpen method does not have to
2812  * allocate the structure; it should just fill it in. Note that the xOpen method must
2813  * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
2814  * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
2815  * element will be valid after xOpen returns regardless of the success or failure of the
2816  * xOpen call.
2817  *
2818  */
2819 struct unqlite_vfs {
2820   const char *zName;       /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
2821   int iVersion;            /* Structure version number (currently 1) */
2822   int szOsFile;            /* Size of subclassed unqlite_file */
2823   int mxPathname;          /* Maximum file pathname length */
2824   int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
2825   int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
2826   int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
2827   int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
2828   int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
2829   int (*xSleep)(unqlite_vfs*, int microseconds);
2830   int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
2831   int (*xGetLastError)(unqlite_vfs*, int, char *);
2832 };
2833 /*
2834  * Flags for the xAccess VFS method
2835  *
2836  * These integer constants can be used as the third parameter to
2837  * the xAccess method of an [unqlite_vfs] object.  They determine
2838  * what kind of permissions the xAccess method is looking for.
2839  * With UNQLITE_ACCESS_EXISTS, the xAccess method
2840  * simply checks whether the file exists.
2841  * With UNQLITE_ACCESS_READWRITE, the xAccess method
2842  * checks whether the named directory is both readable and writable
2843  * (in other words, if files can be added, removed, and renamed within
2844  * the directory).
2845  * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
2846  * [temp_store_directory pragma], though this could change in a future
2847  * release of UnQLite.
2848  * With UNQLITE_ACCESS_READ, the xAccess method
2849  * checks whether the file is readable.  The UNQLITE_ACCESS_READ constant is
2850  * currently unused, though it might be used in a future release of
2851  * UnQLite.
2852  */
2853 #define UNQLITE_ACCESS_EXISTS    0
2854 #define UNQLITE_ACCESS_READWRITE 1
2855 #define UNQLITE_ACCESS_READ      2
2856 /*
2857  * The type used to represent a page number.  The first page in a file
2858  * is called page 1.  0 is used to represent "not a page".
2859  * A page number is an unsigned 64-bit integer.
2860  */
2861 typedef sxu64 pgno;
2862 /*
2863  * A database disk page is represented by an instance
2864  * of the follwoing structure.
2865  */
2866 typedef struct unqlite_page unqlite_page;
2867 struct unqlite_page
2868 {
2869   unsigned char *zData;       /* Content of this page */
2870   void *pUserData;            /* Extra content */
2871   pgno pgno;                  /* Page number for this page */
2872 };
2873 /*
2874  * UnQLite handle to the underlying Key/Value Storage Engine (See below).
2875  */
2876 typedef void * unqlite_kv_handle;
2877 /*
2878  * UnQLite pager IO methods.
2879  *
2880  * An instance of the following structure define the exported methods of the UnQLite pager
2881  * to the underlying Key/Value storage engine.
2882  */
2883 typedef struct unqlite_kv_io unqlite_kv_io;
2884 struct unqlite_kv_io
2885 {
2886 	unqlite_kv_handle  pHandle;     /* UnQLite handle passed as the first parameter to the
2887 									 * method defined below.
2888 									 */
2889 	unqlite_kv_methods *pMethods;   /* Underlying storage engine */
2890 	/* Pager methods */
2891 	int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
2892 	int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
2893 	int (*xNew)(unqlite_kv_handle,unqlite_page **);
2894 	int (*xWrite)(unqlite_page *);
2895 	int (*xDontWrite)(unqlite_page *);
2896 	int (*xDontJournal)(unqlite_page *);
2897 	int (*xDontMkHot)(unqlite_page *);
2898 	int (*xPageRef)(unqlite_page *);
2899 	int (*xPageUnref)(unqlite_page *);
2900 	int (*xPageSize)(unqlite_kv_handle);
2901 	int (*xReadOnly)(unqlite_kv_handle);
2902 	unsigned char * (*xTmpPage)(unqlite_kv_handle);
2903 	void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *));
2904 	void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
2905 	void (*xErr)(unqlite_kv_handle,const char *);
2906 };
2907 /*
2908  * Key/Value Storage Engine Cursor Object
2909  *
2910  * An instance of a subclass of the following object defines a cursor
2911  * used to scan through a key-value storage engine.
2912  */
2913 typedef struct unqlite_kv_cursor unqlite_kv_cursor;
2914 struct unqlite_kv_cursor
2915 {
2916   unqlite_kv_engine *pStore; /* Must be first */
2917   /* Subclasses will typically add additional fields */
2918 };
2919 /*
2920  * Possible seek positions.
2921  */
2922 #define UNQLITE_CURSOR_MATCH_EXACT  1
2923 #define UNQLITE_CURSOR_MATCH_LE     2
2924 #define UNQLITE_CURSOR_MATCH_GE     3
2925 /*
2926  * Key/Value Storage Engine.
2927  *
2928  * A Key-Value storage engine is defined by an instance of the following
2929  * object.
2930  * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
2931  * The storage engine works with key/value pairs where both the key
2932  * and the value are byte arrays of arbitrary length and with no restrictions on content.
2933  * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
2934  * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
2935  * hash-table or Red-black tree storage engine is used for in-memory databases.
2936  * Future versions of UnQLite might add other built-in storage engines (i.e. LSM).
2937  * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
2938  * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
2939  */
2940 struct unqlite_kv_engine
2941 {
2942   const unqlite_kv_io *pIo; /* IO methods: MUST be first */
2943    /* Subclasses will typically add additional fields */
2944 };
2945 /*
2946  * Key/Value Storage Engine Virtual Method Table.
2947  *
2948  * Key/Value storage engine methods is defined by an instance of the following
2949  * object.
2950  * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
2951  * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
2952  */
2953 struct unqlite_kv_methods
2954 {
2955   const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
2956   int szKv;          /* 'unqlite_kv_engine' subclass size */
2957   int szCursor;      /* 'unqlite_kv_cursor' subclass size */
2958   int iVersion;      /* Structure version, currently 1 */
2959   /* Storage engine methods */
2960   int (*xInit)(unqlite_kv_engine *,int iPageSize);
2961   void (*xRelease)(unqlite_kv_engine *);
2962   int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
2963   int (*xOpen)(unqlite_kv_engine *,pgno);
2964   int (*xReplace)(
2965 	  unqlite_kv_engine *,
2966 	  const void *pKey,int nKeyLen,
2967 	  const void *pData,unqlite_int64 nDataLen
2968 	  );
2969     int (*xAppend)(
2970 	  unqlite_kv_engine *,
2971 	  const void *pKey,int nKeyLen,
2972 	  const void *pData,unqlite_int64 nDataLen
2973 	  );
2974   void (*xCursorInit)(unqlite_kv_cursor *);
2975   int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
2976   int (*xFirst)(unqlite_kv_cursor *);
2977   int (*xLast)(unqlite_kv_cursor *);
2978   int (*xValid)(unqlite_kv_cursor *);
2979   int (*xNext)(unqlite_kv_cursor *);
2980   int (*xPrev)(unqlite_kv_cursor *);
2981   int (*xDelete)(unqlite_kv_cursor *);
2982   int (*xKeyLength)(unqlite_kv_cursor *,int *);
2983   int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
2984   int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
2985   int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
2986   void (*xReset)(unqlite_kv_cursor *);
2987   void (*xCursorRelease)(unqlite_kv_cursor *);
2988 };
2989 /*
2990  * UnQLite journal file suffix.
2991  */
2992 #ifndef UNQLITE_JOURNAL_FILE_SUFFIX
2993 #define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
2994 #endif
2995 /*
2996  * Call Context - Error Message Serverity Level.
2997  *
2998  * The following constans are the allowed severity level that can
2999  * passed as the second argument to the [unqlite_context_throw_error()] or
3000  * [unqlite_context_throw_error_format()] interfaces.
3001  * Refer to the official documentation for additional information.
3002  */
3003 #define UNQLITE_CTX_ERR       1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
3004 #define UNQLITE_CTX_WARNING   2 /* Call context Warning */
3005 #define UNQLITE_CTX_NOTICE    3 /* Call context Notice */
3006 /*
3007  * C-API-REF: Please refer to the official documentation for interfaces
3008  * purpose and expected parameters.
3009  */
3010 
3011 /* Database Engine Handle */
3012 UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
3013 UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
3014 UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
3015 
3016 /* Key/Value (KV) Store Interfaces */
3017 UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
3018 UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
3019 UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
3020 UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
3021 UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
3022 UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
3023 	                    int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
3024 UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
3025 UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
3026 
3027 /* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
3028 UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
3029 UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
3030 UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
3031 UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
3032 UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
3033 UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
3034 UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
3035 UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
3036 
3037 /*  Cursor Iterator Interfaces */
3038 UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
3039 UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
3040 UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
3041 UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
3042 UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
3043 UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
3044 UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
3045 UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
3046 UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
3047 UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
3048 UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
3049 UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
3050 UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
3051 UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
3052 
3053 /* Manual Transaction Manager */
3054 UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
3055 UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
3056 UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
3057 
3058 /* Utility interfaces */
3059 UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
3060 UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
3061 UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
3062 UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
3063 
3064 /* In-process extending interfaces */
3065 UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
3066 UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
3067 UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
3068 UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
3069 
3070 /* On Demand Object allocation interfaces */
3071 UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
3072 UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
3073 UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
3074 UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
3075 UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
3076 UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
3077 
3078 /* Dynamically Typed Value Object Management Interfaces */
3079 UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
3080 UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
3081 UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
3082 UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
3083 UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
3084 UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
3085 UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
3086 UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
3087 UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
3088 UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
3089 
3090 /* Foreign Function Parameter Values */
3091 UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
3092 UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
3093 UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
3094 UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
3095 UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
3096 UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
3097 UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
3098 
3099 /* Setting The Result Of A Foreign Function */
3100 UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
3101 UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
3102 UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
3103 UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
3104 UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
3105 UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
3106 UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
3107 UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
3108 UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
3109 
3110 /* Dynamically Typed Value Object Query Interfaces */
3111 UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
3112 UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
3113 UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
3114 UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
3115 UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
3116 UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
3117 UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
3118 UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
3119 UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
3120 UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
3121 UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
3122 UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
3123 
3124 /* JSON Array/Object Management Interfaces */
3125 UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
3126 UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
3127 UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
3128 UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
3129 UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
3130 
3131 /* Call Context Handling Interfaces */
3132 UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
3133 UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
3134 UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
3135 UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
3136 UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
3137 UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
3138 UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
3139 UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
3140 UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
3141 UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
3142 UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
3143 
3144 /* Call Context Memory Management Interfaces */
3145 UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
3146 UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
3147 UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
3148 
3149 /* Global Library Management Interfaces */
3150 UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
3151 UNQLITE_APIEXPORT int unqlite_lib_init(void);
3152 UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
3153 UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
3154 UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
3155 UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
3156 UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
3157 UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
3158 #ifdef __cplusplus
3159 }
3160 #endif /* __cplusplus */
3161 #endif /* _UNQLITE_H_ */
3162 
3163 /* unqliteInt.h */
3164 /*
3165  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3166  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
3167  * Version 1.1.6
3168  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
3169  * please contact Symisc Systems via:
3170  *       legal@symisc.net
3171  *       licensing@symisc.net
3172  *       contact@symisc.net
3173  * or visit:
3174  *      http://unqlite.org/licensing.html
3175  */
3176  /* $SymiscID: unqliteInt.h v1.7 FreeBSD 2012-11-02 11:25 devel <chm@symisc.net> $ */
3177 #ifndef __UNQLITEINT_H__
3178 #define __UNQLITEINT_H__
3179 /* Internal interface definitions for UnQLite. */
3180 #ifdef UNQLITE_AMALGAMATION
3181 /* Marker for routines not intended for external use */
3182 #define UNQLITE_PRIVATE static
3183 #define JX9_AMALGAMATION
3184 #else
3185 #define UNQLITE_PRIVATE
3186 #include "unqlite.h"
3187 #include "jx9Int.h"
3188 #endif
3189 /* forward declaration */
3190 typedef struct unqlite_db unqlite_db;
3191 /*
3192 ** The following values may be passed as the second argument to
3193 ** UnqliteOsLock(). The various locks exhibit the following semantics:
3194 **
3195 ** SHARED:    Any number of processes may hold a SHARED lock simultaneously.
3196 ** RESERVED:  A single process may hold a RESERVED lock on a file at
3197 **            any time. Other processes may hold and obtain new SHARED locks.
3198 ** PENDING:   A single process may hold a PENDING lock on a file at
3199 **            any one time. Existing SHARED locks may persist, but no new
3200 **            SHARED locks may be obtained by other processes.
3201 ** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
3202 **
3203 ** PENDING_LOCK may not be passed directly to UnqliteOsLock(). Instead, a
3204 ** process that requests an EXCLUSIVE lock may actually obtain a PENDING
3205 ** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
3206 ** UnqliteOsLock().
3207 */
3208 #define NO_LOCK         0
3209 #define SHARED_LOCK     1
3210 #define RESERVED_LOCK   2
3211 #define PENDING_LOCK    3
3212 #define EXCLUSIVE_LOCK  4
3213 /*
3214  * UnQLite Locking Strategy (Same as SQLite3)
3215  *
3216  * The following #defines specify the range of bytes used for locking.
3217  * SHARED_SIZE is the number of bytes available in the pool from which
3218  * a random byte is selected for a shared lock.  The pool of bytes for
3219  * shared locks begins at SHARED_FIRST.
3220  *
3221  * The same locking strategy and byte ranges are used for Unix and Windows.
3222  * This leaves open the possiblity of having clients on winNT, and
3223  * unix all talking to the same shared file and all locking correctly.
3224  * To do so would require that samba (or whatever
3225  * tool is being used for file sharing) implements locks correctly between
3226  * windows and unix.  I'm guessing that isn't likely to happen, but by
3227  * using the same locking range we are at least open to the possibility.
3228  *
3229  * Locking in windows is mandatory.  For this reason, we cannot store
3230  * actual data in the bytes used for locking.  The pager never allocates
3231  * the pages involved in locking therefore.  SHARED_SIZE is selected so
3232  * that all locks will fit on a single page even at the minimum page size.
3233  * PENDING_BYTE defines the beginning of the locks.  By default PENDING_BYTE
3234  * is set high so that we don't have to allocate an unused page except
3235  * for very large databases.  But one should test the page skipping logic
3236  * by setting PENDING_BYTE low and running the entire regression suite.
3237  *
3238  * Changing the value of PENDING_BYTE results in a subtly incompatible
3239  * file format.  Depending on how it is changed, you might not notice
3240  * the incompatibility right away, even running a full regression test.
3241  * The default location of PENDING_BYTE is the first byte past the
3242  * 1GB boundary.
3243  */
3244 #define PENDING_BYTE     (0x40000000)
3245 #define RESERVED_BYTE    (PENDING_BYTE+1)
3246 #define SHARED_FIRST     (PENDING_BYTE+2)
3247 #define SHARED_SIZE      510
3248 /*
3249  * The default size of a disk sector in bytes.
3250  */
3251 #ifndef UNQLITE_DEFAULT_SECTOR_SIZE
3252 #define UNQLITE_DEFAULT_SECTOR_SIZE 512
3253 #endif
3254 /*
3255  * Each open database file is managed by a separate instance
3256  * of the "Pager" structure.
3257  */
3258 typedef struct Pager Pager;
3259 /*
3260  * Each database file to be accessed by the system is an instance
3261  * of the following structure.
3262  */
3263 struct unqlite_db
3264 {
3265 	Pager *pPager;              /* Pager and Transaction manager */
3266 	jx9 *pJx9;                  /* Jx9 Engine handle */
3267 	unqlite_kv_cursor *pCursor; /* Database cursor for common usage */
3268 };
3269 /*
3270  * Each database connection is an instance of the following structure.
3271  */
3272 struct unqlite
3273 {
3274 	SyMemBackend sMem;              /* Memory allocator subsystem */
3275 	SyBlob sErr;                    /* Error log */
3276 	unqlite_db sDB;                 /* Storage backend */
3277 #if defined(UNQLITE_ENABLE_THREADS)
3278 	const SyMutexMethods *pMethods;  /* Mutex methods */
3279 	SyMutex *pMutex;                 /* Per-handle mutex */
3280 #endif
3281 	unqlite_vm *pVms;                /* List of active VM */
3282 	sxi32 iVm;                       /* Total number of active VM */
3283 	sxi32 iFlags;                    /* Control flags (See below)  */
3284 	unqlite *pNext,*pPrev;           /* List of active DB handles */
3285 	sxu32 nMagic;                    /* Sanity check against misuse */
3286 };
3287 #define UNQLITE_FL_DISABLE_AUTO_COMMIT   0x001 /* Disable auto-commit on close */
3288 /*
3289  * VM control flags (Mostly related to collection handling).
3290  */
3291 #define UNQLITE_VM_COLLECTION_CREATE     0x001 /* Create a new collection */
3292 #define UNQLITE_VM_COLLECTION_OVERWRITE  0x002 /* Overwrite old collection */
3293 #define UNQLITE_VM_AUTO_LOAD             0x004 /* Auto load a collection from the vfs */
3294 /* Forward declaration */
3295 typedef struct unqlite_col_record unqlite_col_record;
3296 typedef struct unqlite_col unqlite_col;
3297 /*
3298  * Each an in-memory collection record is stored in an instance
3299  * of the following structure.
3300  */
3301 struct unqlite_col_record
3302 {
3303 	unqlite_col *pCol;                      /* Collecion this record belong */
3304 	jx9_int64 nId;                          /* Unique record ID */
3305 	jx9_value sValue;                       /* In-memory value of the record */
3306 	unqlite_col_record *pNextCol,*pPrevCol; /* Collision chain */
3307 	unqlite_col_record *pNext,*pPrev;       /* Linked list of records */
3308 };
3309 /*
3310  * Magic number to identify a valid collection on disk.
3311  */
3312 #define UNQLITE_COLLECTION_MAGIC 0x611E /* sizeof(unsigned short) 2 bytes */
3313 /*
3314  * A loaded collection is identified by an instance of the following structure.
3315  */
3316 struct unqlite_col
3317 {
3318 	unqlite_vm *pVm;   /* VM that own this instance */
3319 	SyString sName;    /* ID of the collection */
3320 	sxu32 nHash;       /* sName hash */
3321 	jx9_value sSchema; /* Collection schema */
3322 	sxu32 nSchemaOfft; /* Shema offset in sHeader */
3323 	SyBlob sWorker;    /* General purpose working buffer */
3324 	SyBlob sHeader;    /* Collection binary header */
3325 	jx9_int64 nLastid; /* Last collection record ID */
3326 	jx9_int64 nCurid;  /* Current record ID */
3327 	jx9_int64 nTotRec; /* Total number of records in the collection */
3328 	int iFlags;        /* Control flags (see below) */
3329 	unqlite_col_record **apRecord; /* Hashtable of loaded records */
3330 	unqlite_col_record *pList;     /* Linked list of records */
3331 	sxu32 nRec;        /* Total number of records in apRecord[] */
3332 	sxu32 nRecSize;    /* apRecord[] size */
3333 	Sytm sCreation;    /* Colleation creation time */
3334 	unqlite_kv_cursor *pCursor; /* Cursor pointing to the raw binary data */
3335 	unqlite_col *pNext,*pPrev;  /* Next and previous collection in the chain */
3336 	unqlite_col *pNextCol,*pPrevCol; /* Collision chain */
3337 };
3338 /*
3339  * Each unQLite Virtual Machine resulting from successful compilation of
3340  * a Jx9 script is represented by an instance of the following structure.
3341  */
3342 struct unqlite_vm
3343 {
3344 	unqlite *pDb;              /* Database handle that own this instance */
3345 	SyMemBackend sAlloc;       /* Private memory allocator */
3346 #if defined(UNQLITE_ENABLE_THREADS)
3347 	SyMutex *pMutex;           /* Recursive mutex associated with this VM. */
3348 #endif
3349 	unqlite_col **apCol;       /* Table of loaded collections */
3350 	unqlite_col *pCol;         /* List of loaded collections */
3351 	sxu32 iCol;                /* Total number of loaded collections */
3352 	sxu32 iColSize;            /* apCol[] size  */
3353 	jx9_vm *pJx9Vm;            /* Compiled Jx9 script*/
3354 	unqlite_vm *pNext,*pPrev;  /* Linked list of active unQLite VM */
3355 	sxu32 nMagic;              /* Magic number to avoid misuse */
3356 };
3357 /*
3358  * Database signature to identify a valid database image.
3359  */
3360 #define UNQLITE_DB_SIG "unqlite"
3361 /*
3362  * Database magic number (4 bytes).
3363  */
3364 #define UNQLITE_DB_MAGIC   0xDB7C2712
3365 /*
3366  * Maximum page size in bytes.
3367  */
3368 #ifdef UNQLITE_MAX_PAGE_SIZE
3369 # undef UNQLITE_MAX_PAGE_SIZE
3370 #endif
3371 #define UNQLITE_MAX_PAGE_SIZE 65536 /* 65K */
3372 /*
3373  * Minimum page size in bytes.
3374  */
3375 #ifdef UNQLITE_MIN_PAGE_SIZE
3376 # undef UNQLITE_MIN_PAGE_SIZE
3377 #endif
3378 #define UNQLITE_MIN_PAGE_SIZE 512
3379 /*
3380  * The default size of a database page.
3381  */
3382 #ifndef UNQLITE_DEFAULT_PAGE_SIZE
3383 # undef UNQLITE_DEFAULT_PAGE_SIZE
3384 #endif
3385 # define UNQLITE_DEFAULT_PAGE_SIZE 4096 /* 4K */
3386 /* Forward declaration */
3387 typedef struct Bitvec Bitvec;
3388 /* Private library functions */
3389 /* api.c */
3390 UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void);
3391 UNQLITE_PRIVATE int unqliteDataConsumer(
3392 	const void *pOut,   /* Data to consume */
3393 	unsigned int nLen,  /* Data length */
3394 	void *pUserData     /* User private data */
3395 	);
3396 UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
3397 	const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
3398 	sxu32 nByte        /* zName length */
3399 	);
3400 UNQLITE_PRIVATE int unqliteGetPageSize(void);
3401 UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr);
3402 UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...);
3403 UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb);
3404 /* unql_vm.c */
3405 UNQLITE_PRIVATE int unqliteCreateCollection(unqlite_vm *pVm,SyString *pName);
3406 UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol);
3407 UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol);
3408 UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(unqlite_col *pCol,jx9_int64 nId);
3409 UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol);
3410 UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol);
3411 UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue);
3412 UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(unqlite_col *pCol,jx9_int64 nId,jx9_value *pValue);
3413 UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(unqlite_vm *pVm,SyString *pCol,int iFlag);
3414 UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue);
3415 UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag);
3416 UNQLITE_PRIVATE int unqliteCollectionDropRecord(unqlite_col *pCol,jx9_int64 nId,int wr_header,int log_err);
3417 UNQLITE_PRIVATE int unqliteCollectionUpdateRecord(unqlite_col *pCol,jx9_int64 nId, jx9_value *pValue,int iFlag);
3418 UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol);
3419 /* unql_jx9.c */
3420 UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm);
3421 /* fastjson.c */
3422 UNQLITE_PRIVATE sxi32 FastJsonEncode(
3423 	jx9_value *pValue, /* Value to encode */
3424 	SyBlob *pOut,      /* Store encoded value here */
3425 	int iNest          /* Nesting limit */
3426 	);
3427 UNQLITE_PRIVATE sxi32 FastJsonDecode(
3428 	const void *pIn, /* Binary JSON  */
3429 	sxu32 nByte,     /* Chunk delimiter */
3430 	jx9_value *pOut, /* Decoded value */
3431 	const unsigned char **pzPtr,
3432 	int iNest /* Nesting limit */
3433 	);
3434 /* vfs.c [io_win.c, io_unix.c ] */
3435 UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void);
3436 /* mem_kv.c */
3437 UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void);
3438 /* lhash_kv.c */
3439 UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void);
3440 /* os.c */
3441 UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
3442 UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
3443 UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size);
3444 UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags);
3445 UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize);
3446 UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType);
3447 UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType);
3448 UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut);
3449 UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id);
3450 UNQLITE_PRIVATE int unqliteOsOpen(
3451   unqlite_vfs *pVfs,
3452   SyMemBackend *pAlloc,
3453   const char *zPath,
3454   unqlite_file **ppOut,
3455   unsigned int flags
3456 );
3457 UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId);
3458 UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync);
3459 UNQLITE_PRIVATE int unqliteOsAccess(unqlite_vfs *pVfs,const char *zPath,int flags,int *pResOut);
3460 /* bitmap.c */
3461 UNQLITE_PRIVATE Bitvec *unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize);
3462 UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i);
3463 UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i);
3464 UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p);
3465 /* pager.c */
3466 UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut);
3467 UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur);
3468 UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage);
3469 UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager);
3470 UNQLITE_PRIVATE int unqlitePagerOpen(
3471   unqlite_vfs *pVfs,       /* The virtual file system to use */
3472   unqlite *pDb,            /* Database handle */
3473   const char *zFilename,   /* Name of the database file to open */
3474   unsigned int iFlags      /* flags controlling this file */
3475   );
3476 UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods);
3477 UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb);
3478 UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager);
3479 UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager);
3480 UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine);
3481 UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen);
3482 UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager);
3483 #endif /* __UNQLITEINT_H__ */
3484 
3485 /* api.c */
3486 /*
3487  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
3488  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
3489  * Version 1.1.6
3490  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
3491  * please contact Symisc Systems via:
3492  *       legal@symisc.net
3493  *       licensing@symisc.net
3494  *       contact@symisc.net
3495  * or visit:
3496  *      http://unqlite.org/licensing.html
3497  */
3498  /* $SymiscID: api.c v2.0 FreeBSD 2012-11-08 23:07 stable <chm@symisc.net> $ */
3499 #ifndef UNQLITE_AMALGAMATION
3500 #include "unqliteInt.h"
3501 #endif
3502 /* This file implement the public interfaces presented to host-applications.
3503  * Routines in other files are for internal use by UnQLite and should not be
3504  * accessed by users of the library.
3505  */
3506 #define UNQLITE_DB_MISUSE(DB) (DB == 0 || DB->nMagic != UNQLITE_DB_MAGIC)
3507 #define UNQLITE_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
3508 /* If another thread have released a working instance, the following macros
3509  * evaluates to true. These macros are only used when the library
3510  * is built with threading support enabled.
3511  */
3512 #define UNQLITE_THRD_DB_RELEASE(DB) (DB->nMagic != UNQLITE_DB_MAGIC)
3513 #define UNQLITE_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
3514 /* IMPLEMENTATION: unqlite@embedded@symisc 118-09-4785 */
3515 /*
3516  * All global variables are collected in the structure named "sUnqlMPGlobal".
3517  * That way it is clear in the code when we are using static variable because
3518  * its name start with sUnqlMPGlobal.
3519  */
3520 static struct unqlGlobal_Data
3521 {
3522 	SyMemBackend sAllocator;                /* Global low level memory allocator */
3523 #if defined(UNQLITE_ENABLE_THREADS)
3524 	const SyMutexMethods *pMutexMethods;   /* Mutex methods */
3525 	SyMutex *pMutex;                       /* Global mutex */
3526 	sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded
3527 										    * The threading level can be set using the [unqlite_lib_config()]
3528 											* interface with a configuration verb set to
3529 											* UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or
3530 											* UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
3531 											*/
3532 #endif
3533 	SySet kv_storage;                      /* Installed KV storage engines */
3534 	int iPageSize;                         /* Default Page size */
3535 	unqlite_vfs *pVfs;                     /* Underlying virtual file system (Vfs) */
3536 	sxi32 nDB;                             /* Total number of active DB handles */
3537 	unqlite *pDB;                          /* List of active DB handles */
3538 	sxu32 nMagic;                          /* Sanity check against library misuse */
3539 }sUnqlMPGlobal = {
3540 	{0, 0, 0, 0, 0, 0, 0, 0, {0}},
3541 #if defined(UNQLITE_ENABLE_THREADS)
3542 	0,
3543 	0,
3544 	0,
3545 #endif
3546 	{0, 0, 0, 0, 0, 0, 0 },
3547 	UNQLITE_DEFAULT_PAGE_SIZE,
3548 	0,
3549 	0,
3550 	0,
3551 	0
3552 };
3553 #define UNQLITE_LIB_MAGIC  0xEA1495BA
3554 #define UNQLITE_LIB_MISUSE (sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC)
3555 /*
3556  * Supported threading level.
3557  * These options have meaning only when the library is compiled with multi-threading
3558  * support. That is, the UNQLITE_ENABLE_THREADS compile time directive must be defined
3559  * when UnQLite is built.
3560  * UNQLITE_THREAD_LEVEL_SINGLE:
3561  *  In this mode, mutexing is disabled and the library can only be used by a single thread.
3562  * UNQLITE_THREAD_LEVEL_MULTI
3563  *  In this mode, all mutexes including the recursive mutexes on [unqlite] objects
3564  *  are enabled so that the application is free to share the same database handle
3565  *  between different threads at the same time.
3566  */
3567 #define UNQLITE_THREAD_LEVEL_SINGLE 1
3568 #define UNQLITE_THREAD_LEVEL_MULTI  2
3569 /*
3570  * Find a Key Value storage engine from the set of installed engines.
3571  * Return a pointer to the storage engine methods on success. NULL on failure.
3572  */
unqliteFindKVStore(const char * zName,sxu32 nByte)3573 UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
3574 	const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
3575 	sxu32 nByte /* zName length */
3576 	)
3577 {
3578 	unqlite_kv_methods **apStore,*pEntry;
3579 	sxu32 n,nMax;
3580 	/* Point to the set of installed engines */
3581 	apStore = (unqlite_kv_methods **)SySetBasePtr(&sUnqlMPGlobal.kv_storage);
3582 	nMax = SySetUsed(&sUnqlMPGlobal.kv_storage);
3583 	for( n = 0 ; n < nMax; ++n ){
3584 		pEntry = apStore[n];
3585 		if( nByte == SyStrlen(pEntry->zName) && SyStrnicmp(pEntry->zName,zName,nByte) == 0 ){
3586 			/* Storage engine found */
3587 			return pEntry;
3588 		}
3589 	}
3590 	/* No such entry, return NULL */
3591 	return 0;
3592 }
3593 /*
3594  * Configure the UnQLite library.
3595  * Return UNQLITE_OK on success. Any other return value indicates failure.
3596  * Refer to [unqlite_lib_config()].
3597  */
unqliteCoreConfigure(sxi32 nOp,va_list ap)3598 static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap)
3599 {
3600 	int rc = UNQLITE_OK;
3601 	switch(nOp){
3602 	    case UNQLITE_LIB_CONFIG_PAGE_SIZE: {
3603 			/* Default page size: Must be a power of two */
3604 			int iPage = va_arg(ap,int);
3605 			if( iPage >= UNQLITE_MIN_PAGE_SIZE && iPage <= UNQLITE_MAX_PAGE_SIZE ){
3606 				if( !(iPage & (iPage - 1)) ){
3607 					sUnqlMPGlobal.iPageSize = iPage;
3608 				}else{
3609 					/* Invalid page size */
3610 					rc = UNQLITE_INVALID;
3611 				}
3612 			}else{
3613 				/* Invalid page size */
3614 				rc = UNQLITE_INVALID;
3615 			}
3616 			break;
3617 										   }
3618 	    case UNQLITE_LIB_CONFIG_STORAGE_ENGINE: {
3619 			/* Install a key value storage engine */
3620 			unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *);
3621 			/* Make sure we are delaing with a valid methods */
3622 			if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0
3623 				|| pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0
3624 				|| pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){
3625 					rc = UNQLITE_INVALID;
3626 					break;
3627 			}
3628 			/* Install it */
3629 			rc = SySetPut(&sUnqlMPGlobal.kv_storage,(const void *)&pMethods);
3630 			break;
3631 												}
3632 	    case UNQLITE_LIB_CONFIG_VFS:{
3633 			/* Install a virtual file system */
3634 			unqlite_vfs *pVfs = va_arg(ap,unqlite_vfs *);
3635 			if( pVfs ){
3636 			 sUnqlMPGlobal.pVfs = pVfs;
3637 			}
3638 			break;
3639 								}
3640 		case UNQLITE_LIB_CONFIG_USER_MALLOC: {
3641 			/* Use an alternative low-level memory allocation routines */
3642 			const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
3643 			/* Save the memory failure callback (if available) */
3644 			ProcMemError xMemErr = sUnqlMPGlobal.sAllocator.xMemError;
3645 			void *pMemErr = sUnqlMPGlobal.sAllocator.pUserData;
3646 			if( pMethods == 0 ){
3647 				/* Use the built-in memory allocation subsystem */
3648 				rc = SyMemBackendInit(&sUnqlMPGlobal.sAllocator, xMemErr, pMemErr);
3649 			}else{
3650 				rc = SyMemBackendInitFromOthers(&sUnqlMPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
3651 			}
3652 			break;
3653 										  }
3654 		case UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK: {
3655 			/* Memory failure callback */
3656 			ProcMemError xMemErr = va_arg(ap, ProcMemError);
3657 			void *pUserData = va_arg(ap, void *);
3658 			sUnqlMPGlobal.sAllocator.xMemError = xMemErr;
3659 			sUnqlMPGlobal.sAllocator.pUserData = pUserData;
3660 			break;
3661 												 }
3662 		case UNQLITE_LIB_CONFIG_USER_MUTEX: {
3663 #if defined(UNQLITE_ENABLE_THREADS)
3664 			/* Use an alternative low-level mutex subsystem */
3665 			const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
3666 #if defined (UNTRUST)
3667 			if( pMethods == 0 ){
3668 				rc = UNQLITE_CORRUPT;
3669 			}
3670 #endif
3671 			/* Sanity check */
3672 			if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
3673 				/* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
3674 				rc = UNQLITE_CORRUPT;
3675 				break;
3676 			}
3677 			if( sUnqlMPGlobal.pMutexMethods ){
3678 				/* Overwrite the previous mutex subsystem */
3679 				SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
3680 				if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
3681 					sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
3682 				}
3683 				sUnqlMPGlobal.pMutex = 0;
3684 			}
3685 			/* Initialize and install the new mutex subsystem */
3686 			if( pMethods->xGlobalInit ){
3687 				rc = pMethods->xGlobalInit();
3688 				if ( rc != UNQLITE_OK ){
3689 					break;
3690 				}
3691 			}
3692 			/* Create the global mutex */
3693 			sUnqlMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
3694 			if( sUnqlMPGlobal.pMutex == 0 ){
3695 				/*
3696 				 * If the supplied mutex subsystem is so sick that we are unable to
3697 				 * create a single mutex, there is no much we can do here.
3698 				 */
3699 				if( pMethods->xGlobalRelease ){
3700 					pMethods->xGlobalRelease();
3701 				}
3702 				rc = UNQLITE_CORRUPT;
3703 				break;
3704 			}
3705 			sUnqlMPGlobal.pMutexMethods = pMethods;
3706 			if( sUnqlMPGlobal.nThreadingLevel == 0 ){
3707 				/* Set a default threading level */
3708 				sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
3709 			}
3710 #endif
3711 			break;
3712 										   }
3713 		case UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE:
3714 #if defined(UNQLITE_ENABLE_THREADS)
3715 			/* Single thread mode (Only one thread is allowed to play with the library) */
3716 			sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_SINGLE;
3717 			jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE);
3718 #endif
3719 			break;
3720 		case UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI:
3721 #if defined(UNQLITE_ENABLE_THREADS)
3722 			/* Multi-threading mode (library is thread safe and database handles and virtual machines
3723 			 * may be shared between multiple threads).
3724 			 */
3725 			sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
3726 			jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_MULTI);
3727 #endif
3728 			break;
3729 		default:
3730 			/* Unknown configuration option */
3731 			rc = UNQLITE_CORRUPT;
3732 			break;
3733 	}
3734 	return rc;
3735 }
3736 /*
3737  * [CAPIREF: unqlite_lib_config()]
3738  * Please refer to the official documentation for function purpose and expected parameters.
3739  */
unqlite_lib_config(int nConfigOp,...)3740 int unqlite_lib_config(int nConfigOp,...)
3741 {
3742 	va_list ap;
3743 	int rc;
3744 	if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
3745 		/* Library is already initialized, this operation is forbidden */
3746 		return UNQLITE_LOCKED;
3747 	}
3748 	va_start(ap,nConfigOp);
3749 	rc = unqliteCoreConfigure(nConfigOp,ap);
3750 	va_end(ap);
3751 	return rc;
3752 }
3753 /*
3754  * Global library initialization
3755  * Refer to [unqlite_lib_init()]
3756  * This routine must be called to initialize the memory allocation subsystem, the mutex
3757  * subsystem prior to doing any serious work with the library. The first thread to call
3758  * this routine does the initialization process and set the magic number so no body later
3759  * can re-initialize the library. If subsequent threads call this  routine before the first
3760  * thread have finished the initialization process, then the subsequent threads must block
3761  * until the initialization process is done.
3762  */
unqliteCoreInitialize(void)3763 static sxi32 unqliteCoreInitialize(void)
3764 {
3765 	const unqlite_kv_methods *pMethods;
3766 	const unqlite_vfs *pVfs; /* Built-in vfs */
3767 #if defined(UNQLITE_ENABLE_THREADS)
3768 	const SyMutexMethods *pMutexMethods = 0;
3769 	SyMutex *pMaster = 0;
3770 #endif
3771 	int rc;
3772 	/*
3773 	 * If the library is already initialized, then a call to this routine
3774 	 * is a no-op.
3775 	 */
3776 	if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
3777 		return UNQLITE_OK; /* Already initialized */
3778 	}
3779 	/* Point to the built-in vfs */
3780 	pVfs = unqliteExportBuiltinVfs();
3781 	/* Install it */
3782 	unqlite_lib_config(UNQLITE_LIB_CONFIG_VFS, pVfs);
3783 #if defined(UNQLITE_ENABLE_THREADS)
3784 	if( sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_SINGLE ){
3785 		pMutexMethods = sUnqlMPGlobal.pMutexMethods;
3786 		if( pMutexMethods == 0 ){
3787 			/* Use the built-in mutex subsystem */
3788 			pMutexMethods = SyMutexExportMethods();
3789 			if( pMutexMethods == 0 ){
3790 				return UNQLITE_CORRUPT; /* Can't happen */
3791 			}
3792 			/* Install the mutex subsystem */
3793 			rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MUTEX, pMutexMethods);
3794 			if( rc != UNQLITE_OK ){
3795 				return rc;
3796 			}
3797 		}
3798 		/* Obtain a static mutex so we can initialize the library without calling malloc() */
3799 		pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
3800 		if( pMaster == 0 ){
3801 			return UNQLITE_CORRUPT; /* Can't happen */
3802 		}
3803 	}
3804 	/* Lock the master mutex */
3805 	rc = UNQLITE_OK;
3806 	SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
3807 	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
3808 #endif
3809 		if( sUnqlMPGlobal.sAllocator.pMethods == 0 ){
3810 			/* Install a memory subsystem */
3811 			rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
3812 			if( rc != UNQLITE_OK ){
3813 				/* If we are unable to initialize the memory backend, there is no much we can do here.*/
3814 				goto End;
3815 			}
3816 		}
3817 #if defined(UNQLITE_ENABLE_THREADS)
3818 		if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
3819 			/* Protect the memory allocation subsystem */
3820 			rc = SyMemBackendMakeThreadSafe(&sUnqlMPGlobal.sAllocator, sUnqlMPGlobal.pMutexMethods);
3821 			if( rc != UNQLITE_OK ){
3822 				goto End;
3823 			}
3824 		}
3825 #endif
3826 		SySetInit(&sUnqlMPGlobal.kv_storage,&sUnqlMPGlobal.sAllocator,sizeof(unqlite_kv_methods *));
3827 		/* Install the built-in Key Value storage engines */
3828 		pMethods = unqliteExportMemKvStorage(); /* In-memory storage */
3829 		unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
3830 		/* Default disk key/value storage engine */
3831 		pMethods = unqliteExportDiskKvStorage(); /* Disk storage */
3832 		unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
3833 		/* Default page size */
3834 		if( sUnqlMPGlobal.iPageSize < UNQLITE_MIN_PAGE_SIZE ){
3835 			unqlite_lib_config(UNQLITE_LIB_CONFIG_PAGE_SIZE,UNQLITE_DEFAULT_PAGE_SIZE);
3836 		}
3837 		/* Our library is initialized, set the magic number */
3838 		sUnqlMPGlobal.nMagic = UNQLITE_LIB_MAGIC;
3839 		rc = UNQLITE_OK;
3840 #if defined(UNQLITE_ENABLE_THREADS)
3841 	} /* sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC */
3842 #endif
3843 End:
3844 #if defined(UNQLITE_ENABLE_THREADS)
3845 	/* Unlock the master mutex */
3846 	SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
3847 #endif
3848 	return rc;
3849 }
3850 /* Forward declaration */
3851 static int unqliteVmRelease(unqlite_vm *pVm);
3852 /*
3853  * Release a single instance of an unqlite database handle.
3854  */
unqliteDbRelease(unqlite * pDb)3855 static int unqliteDbRelease(unqlite *pDb)
3856 {
3857 	unqlite_db *pStore = &pDb->sDB;
3858 	unqlite_vm *pVm,*pNext;
3859 	int rc = UNQLITE_OK;
3860 	if( (pDb->iFlags & UNQLITE_FL_DISABLE_AUTO_COMMIT) == 0 ){
3861 		/* Commit any outstanding transaction */
3862 		rc = unqlitePagerCommit(pStore->pPager);
3863 		if( rc != UNQLITE_OK ){
3864 			/* Rollback the transaction */
3865 			rc = unqlitePagerRollback(pStore->pPager,FALSE);
3866 		}
3867 	}else{
3868 		/* Rollback any outstanding transaction */
3869 		rc = unqlitePagerRollback(pStore->pPager,FALSE);
3870 	}
3871 	/* Close the pager */
3872 	unqlitePagerClose(pStore->pPager);
3873 	/* Release any active VM's */
3874 	pVm = pDb->pVms;
3875 	for(;;){
3876 		if( pDb->iVm < 1 ){
3877 			break;
3878 		}
3879 		/* Point to the next entry */
3880 		pNext = pVm->pNext;
3881 		unqliteVmRelease(pVm);
3882 		pVm = pNext;
3883 		pDb->iVm--;
3884 	}
3885 	/* Release the Jx9 handle */
3886 	jx9_release(pStore->pJx9);
3887 	/* Set a dummy magic number */
3888 	pDb->nMagic = 0x7250;
3889 	/* Release the whole memory subsystem */
3890 	SyMemBackendRelease(&pDb->sMem);
3891 	/* Commit or rollback result */
3892 	return rc;
3893 }
3894 /*
3895  * Release all resources consumed by the library.
3896  * Note: This call is not thread safe. Refer to [unqlite_lib_shutdown()].
3897  */
unqliteCoreShutdown(void)3898 static void unqliteCoreShutdown(void)
3899 {
3900 	unqlite *pDb, *pNext;
3901 	/* Release all active databases handles */
3902 	pDb = sUnqlMPGlobal.pDB;
3903 	for(;;){
3904 		if( sUnqlMPGlobal.nDB < 1 ){
3905 			break;
3906 		}
3907 		pNext = pDb->pNext;
3908 		unqliteDbRelease(pDb);
3909 		pDb = pNext;
3910 		sUnqlMPGlobal.nDB--;
3911 	}
3912 	/* Release the storage methods container */
3913 	SySetRelease(&sUnqlMPGlobal.kv_storage);
3914 #if defined(UNQLITE_ENABLE_THREADS)
3915 	/* Release the mutex subsystem */
3916 	if( sUnqlMPGlobal.pMutexMethods ){
3917 		if( sUnqlMPGlobal.pMutex ){
3918 			SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
3919 			sUnqlMPGlobal.pMutex = 0;
3920 		}
3921 		if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
3922 			sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
3923 		}
3924 		sUnqlMPGlobal.pMutexMethods = 0;
3925 	}
3926 	sUnqlMPGlobal.nThreadingLevel = 0;
3927 #endif
3928 	if( sUnqlMPGlobal.sAllocator.pMethods ){
3929 		/* Release the memory backend */
3930 		SyMemBackendRelease(&sUnqlMPGlobal.sAllocator);
3931 	}
3932 	sUnqlMPGlobal.nMagic = 0x1764;
3933 	/* Finally, shutdown the Jx9 library */
3934 	jx9_lib_shutdown();
3935 }
3936 /*
3937  * [CAPIREF: unqlite_lib_init()]
3938  * Please refer to the official documentation for function purpose and expected parameters.
3939  */
unqlite_lib_init(void)3940 int unqlite_lib_init(void)
3941 {
3942 	int rc;
3943 	rc = unqliteCoreInitialize();
3944 	return rc;
3945 }
3946 /*
3947  * [CAPIREF: unqlite_lib_shutdown()]
3948  * Please refer to the official documentation for function purpose and expected parameters.
3949  */
unqlite_lib_shutdown(void)3950 int unqlite_lib_shutdown(void)
3951 {
3952 	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
3953 		/* Already shut */
3954 		return UNQLITE_OK;
3955 	}
3956 	unqliteCoreShutdown();
3957 	return UNQLITE_OK;
3958 }
3959 /*
3960  * [CAPIREF: unqlite_lib_is_threadsafe()]
3961  * Please refer to the official documentation for function purpose and expected parameters.
3962  */
unqlite_lib_is_threadsafe(void)3963 int unqlite_lib_is_threadsafe(void)
3964 {
3965 	if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
3966 		return 0;
3967 	}
3968 #if defined(UNQLITE_ENABLE_THREADS)
3969 		if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
3970 			/* Muli-threading support is enabled */
3971 			return 1;
3972 		}else{
3973 			/* Single-threading */
3974 			return 0;
3975 		}
3976 #else
3977 	return 0;
3978 #endif
3979 }
3980 /*
3981  *
3982  * [CAPIREF: unqlite_lib_version()]
3983  * Please refer to the official documentation for function purpose and expected parameters.
3984  */
unqlite_lib_version(void)3985 const char * unqlite_lib_version(void)
3986 {
3987 	return UNQLITE_VERSION;
3988 }
3989 /*
3990  *
3991  * [CAPIREF: unqlite_lib_signature()]
3992  * Please refer to the official documentation for function purpose and expected parameters.
3993  */
unqlite_lib_signature(void)3994 const char * unqlite_lib_signature(void)
3995 {
3996 	return UNQLITE_SIG;
3997 }
3998 /*
3999  *
4000  * [CAPIREF: unqlite_lib_ident()]
4001  * Please refer to the official documentation for function purpose and expected parameters.
4002  */
unqlite_lib_ident(void)4003 const char * unqlite_lib_ident(void)
4004 {
4005 	return UNQLITE_IDENT;
4006 }
4007 /*
4008  *
4009  * [CAPIREF: unqlite_lib_copyright()]
4010  * Please refer to the official documentation for function purpose and expected parameters.
4011  */
unqlite_lib_copyright(void)4012 const char * unqlite_lib_copyright(void)
4013 {
4014 	return UNQLITE_COPYRIGHT;
4015 }
4016 /*
4017  * Remove harmfull and/or stale flags passed to the [unqlite_open()] interface.
4018  */
unqliteSanityzeFlag(unsigned int iFlags)4019 static unsigned int unqliteSanityzeFlag(unsigned int iFlags)
4020 {
4021 	iFlags &= ~UNQLITE_OPEN_EXCLUSIVE; /* Reserved flag */
4022 	if( iFlags & UNQLITE_OPEN_TEMP_DB ){
4023 		/* Omit journaling for temporary database */
4024 		iFlags |= UNQLITE_OPEN_OMIT_JOURNALING|UNQLITE_OPEN_CREATE;
4025 	}
4026 	if( (iFlags & (UNQLITE_OPEN_READONLY|UNQLITE_OPEN_READWRITE)) == 0 ){
4027 		/* Auto-append the R+W flag */
4028 		iFlags |= UNQLITE_OPEN_READWRITE;
4029 	}
4030 	if( iFlags & UNQLITE_OPEN_CREATE ){
4031 		iFlags &= ~(UNQLITE_OPEN_MMAP|UNQLITE_OPEN_READONLY);
4032 		/* Auto-append the R+W flag */
4033 		iFlags |= UNQLITE_OPEN_READWRITE;
4034 	}else{
4035 		if( iFlags & UNQLITE_OPEN_READONLY ){
4036 			iFlags &= ~UNQLITE_OPEN_READWRITE;
4037 		}else if( iFlags & UNQLITE_OPEN_READWRITE ){
4038 			iFlags &= ~UNQLITE_OPEN_MMAP;
4039 		}
4040 	}
4041 	return iFlags;
4042 }
4043 /*
4044  * This routine does the work of initializing a database handle on behalf
4045  * of [unqlite_open()].
4046  */
unqliteInitDatabase(unqlite * pDB,SyMemBackend * pParent,const char * zFilename,unsigned int iFlags)4047 static int unqliteInitDatabase(
4048 	unqlite *pDB,            /* Database handle */
4049 	SyMemBackend *pParent,   /* Master memory backend */
4050 	const char *zFilename,   /* Target database */
4051 	unsigned int iFlags      /* Open flags */
4052 	)
4053 {
4054 	unqlite_db *pStorage = &pDB->sDB;
4055 	int rc;
4056 	/* Initialiaze the memory subsystem */
4057 	SyMemBackendInitFromParent(&pDB->sMem,pParent);
4058 #if defined(UNQLITE_ENABLE_THREADS)
4059 	/* No need for internal mutexes */
4060 	SyMemBackendDisbaleMutexing(&pDB->sMem);
4061 #endif
4062 	SyBlobInit(&pDB->sErr,&pDB->sMem);
4063 	/* Sanityze flags */
4064 	iFlags = unqliteSanityzeFlag(iFlags);
4065 	/* Init the pager and the transaction manager */
4066 	rc = unqlitePagerOpen(sUnqlMPGlobal.pVfs,pDB,zFilename,iFlags);
4067 	if( rc != UNQLITE_OK ){
4068 		return rc;
4069 	}
4070 	/* Allocate a new Jx9 engine handle */
4071 	rc = jx9_init(&pStorage->pJx9);
4072 	if( rc != JX9_OK ){
4073 		return rc;
4074 	}
4075 	return UNQLITE_OK;
4076 }
4077 /*
4078  * Allocate and initialize a new UnQLite Virtual Mahcine and attach it
4079  * to the compiled Jx9 script.
4080  */
unqliteInitVm(unqlite * pDb,jx9_vm * pJx9Vm,unqlite_vm ** ppOut)4081 static int unqliteInitVm(unqlite *pDb,jx9_vm *pJx9Vm,unqlite_vm **ppOut)
4082 {
4083 	unqlite_vm *pVm;
4084 
4085 	*ppOut = 0;
4086 	/* Allocate a new VM instance */
4087 	pVm = (unqlite_vm *)SyMemBackendPoolAlloc(&pDb->sMem,sizeof(unqlite_vm));
4088 	if( pVm == 0 ){
4089 		return UNQLITE_NOMEM;
4090 	}
4091 	/* Zero the structure */
4092 	SyZero(pVm,sizeof(unqlite_vm));
4093 	/* Initialize */
4094 	SyMemBackendInitFromParent(&pVm->sAlloc,&pDb->sMem);
4095 	/* Allocate a new collection table */
4096 	pVm->apCol = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc,32 * sizeof(unqlite_col *));
4097 	if( pVm->apCol == 0 ){
4098 		goto fail;
4099 	}
4100 	pVm->iColSize = 32; /* Must be a power of two */
4101 	/* Zero the table */
4102 	SyZero((void *)pVm->apCol,pVm->iColSize * sizeof(unqlite_col *));
4103 #if defined(UNQLITE_ENABLE_THREADS)
4104 	if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
4105 		 /* Associate a recursive mutex with this instance */
4106 		 pVm->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
4107 		 if( pVm->pMutex == 0 ){
4108 			 goto fail;
4109 		 }
4110 	 }
4111 #endif
4112 	/* Link the VM to the list of active virtual machines */
4113 	pVm->pJx9Vm = pJx9Vm;
4114 	pVm->pDb = pDb;
4115 	MACRO_LD_PUSH(pDb->pVms,pVm);
4116 	pDb->iVm++;
4117 	/* Register Jx9 functions */
4118 	unqliteRegisterJx9Functions(pVm);
4119 	/* Set the magic number */
4120 	pVm->nMagic = JX9_VM_INIT; /* Same magic number as Jx9 */
4121 	/* All done */
4122 	*ppOut = pVm;
4123 	return UNQLITE_OK;
4124 fail:
4125 	SyMemBackendRelease(&pVm->sAlloc);
4126 	SyMemBackendPoolFree(&pDb->sMem,pVm);
4127 	return UNQLITE_NOMEM;
4128 }
4129 /*
4130  * Release an active VM.
4131  */
unqliteVmRelease(unqlite_vm * pVm)4132 static int unqliteVmRelease(unqlite_vm *pVm)
4133 {
4134 	/* Release the Jx9 VM */
4135 	jx9_vm_release(pVm->pJx9Vm);
4136 	/* Release the private memory backend */
4137 	SyMemBackendRelease(&pVm->sAlloc);
4138 	/* Upper layer will discard this VM from the list
4139 	 * of active VM.
4140 	 */
4141 	return UNQLITE_OK;
4142 }
4143 /*
4144  * Return the default page size.
4145  */
unqliteGetPageSize(void)4146 UNQLITE_PRIVATE int unqliteGetPageSize(void)
4147 {
4148 	int iSize =  sUnqlMPGlobal.iPageSize;
4149 	if( iSize < UNQLITE_MIN_PAGE_SIZE || iSize > UNQLITE_MAX_PAGE_SIZE ){
4150 		iSize = UNQLITE_DEFAULT_PAGE_SIZE;
4151 	}
4152 	return iSize;
4153 }
4154 /*
4155  * Generate an error message.
4156  */
unqliteGenError(unqlite * pDb,const char * zErr)4157 UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr)
4158 {
4159 	int rc;
4160 	/* Append the error message */
4161 	rc = SyBlobAppend(&pDb->sErr,(const void *)zErr,SyStrlen(zErr));
4162 	/* Append a new line */
4163 	SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
4164 	return rc;
4165 }
4166 /*
4167  * Generate an error message (Printf like).
4168  */
unqliteGenErrorFormat(unqlite * pDb,const char * zFmt,...)4169 UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...)
4170 {
4171 	va_list ap;
4172 	int rc;
4173 	va_start(ap,zFmt);
4174 	rc = SyBlobFormatAp(&pDb->sErr,zFmt,ap);
4175 	va_end(ap);
4176 	/* Append a new line */
4177 	SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
4178 	return rc;
4179 }
4180 /*
4181  * Generate an error message (Out of memory).
4182  */
unqliteGenOutofMem(unqlite * pDb)4183 UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb)
4184 {
4185 	int rc;
4186 	rc = unqliteGenError(pDb,"unQLite is running out of memory");
4187 	return rc;
4188 }
4189 /*
4190  * Configure a working UnQLite database handle.
4191  */
unqliteConfigure(unqlite * pDb,int nOp,va_list ap)4192 static int unqliteConfigure(unqlite *pDb,int nOp,va_list ap)
4193 {
4194 	int rc = UNQLITE_OK;
4195 	switch(nOp){
4196 	case UNQLITE_CONFIG_JX9_ERR_LOG:
4197 		/* Jx9 compile-time error log */
4198 		rc = jx9EngineConfig(pDb->sDB.pJx9,JX9_CONFIG_ERR_LOG,ap);
4199 		break;
4200 	case UNQLITE_CONFIG_MAX_PAGE_CACHE: {
4201 		int max_page = va_arg(ap,int);
4202 		/* Maximum number of page to cache (Simple hint). */
4203 		rc = unqlitePagerSetCachesize(pDb->sDB.pPager,max_page);
4204 		break;
4205 										}
4206 	case UNQLITE_CONFIG_ERR_LOG: {
4207 		/* Database error log if any */
4208 		const char **pzPtr = va_arg(ap, const char **);
4209 		int *pLen = va_arg(ap, int *);
4210 		if( pzPtr == 0 ){
4211 			rc = JX9_CORRUPT;
4212 			break;
4213 		}
4214 		/* NULL terminate the error-log buffer */
4215 		SyBlobNullAppend(&pDb->sErr);
4216 		/* Point to the error-log buffer */
4217 		*pzPtr = (const char *)SyBlobData(&pDb->sErr);
4218 		if( pLen ){
4219 			if( SyBlobLength(&pDb->sErr) > 1 /* NULL '\0' terminator */ ){
4220 				*pLen = (int)SyBlobLength(&pDb->sErr);
4221 			}else{
4222 				*pLen = 0;
4223 			}
4224 		}
4225 		break;
4226 								 }
4227 	case UNQLITE_CONFIG_DISABLE_AUTO_COMMIT:{
4228 		/* Disable auto-commit */
4229 		pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
4230 		break;
4231 											}
4232 	case UNQLITE_CONFIG_GET_KV_NAME: {
4233 		/* Name of the underlying KV storage engine */
4234 		const char **pzPtr = va_arg(ap,const char **);
4235 		if( pzPtr ){
4236 			unqlite_kv_engine *pEngine;
4237 			pEngine = unqlitePagerGetKvEngine(pDb);
4238 			/* Point to the name */
4239 			*pzPtr = pEngine->pIo->pMethods->zName;
4240 		}
4241 		break;
4242 									 }
4243 	default:
4244 		/* Unknown configuration option */
4245 		rc = UNQLITE_UNKNOWN;
4246 		break;
4247 	}
4248 	return rc;
4249 }
4250 /*
4251  * Export the global (master) memory allocator to submodules.
4252  */
unqliteExportMemBackend(void)4253 UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void)
4254 {
4255 	return &sUnqlMPGlobal.sAllocator;
4256 }
4257 /*
4258  * [CAPIREF: unqlite_open()]
4259  * Please refer to the official documentation for function purpose and expected parameters.
4260  */
unqlite_open(unqlite ** ppDB,const char * zFilename,unsigned int iMode)4261 int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode)
4262 {
4263 	unqlite *pHandle;
4264 	int rc;
4265 #if defined(UNTRUST)
4266 	if( ppDB == 0 ){
4267 		return UNQLITE_CORRUPT;
4268 	}
4269 #endif
4270 	*ppDB = 0;
4271 	/* One-time automatic library initialization */
4272 	rc = unqliteCoreInitialize();
4273 	if( rc != UNQLITE_OK ){
4274 		return rc;
4275 	}
4276 	/* Allocate a new database handle */
4277 	pHandle = (unqlite *)SyMemBackendPoolAlloc(&sUnqlMPGlobal.sAllocator, sizeof(unqlite));
4278 	if( pHandle == 0 ){
4279 		return UNQLITE_NOMEM;
4280 	}
4281 	/* Zero the structure */
4282 	SyZero(pHandle,sizeof(unqlite));
4283 	if( iMode < 1 ){
4284 		/* Assume a read-only database */
4285 		iMode = UNQLITE_OPEN_READONLY|UNQLITE_OPEN_MMAP;
4286 	}
4287 	/* Init the database */
4288 	rc = unqliteInitDatabase(pHandle,&sUnqlMPGlobal.sAllocator,zFilename,iMode);
4289 	if( rc != UNQLITE_OK ){
4290 		goto Release;
4291 	}
4292 #if defined(UNQLITE_ENABLE_THREADS)
4293 	if( !(iMode & UNQLITE_OPEN_NOMUTEX) && (sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE) ){
4294 		 /* Associate a recursive mutex with this instance */
4295 		 pHandle->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
4296 		 if( pHandle->pMutex == 0 ){
4297 			 rc = UNQLITE_NOMEM;
4298 			 goto Release;
4299 		 }
4300 	 }
4301 #endif
4302 	/* Link to the list of active DB handles */
4303 #if defined(UNQLITE_ENABLE_THREADS)
4304 	/* Enter the global mutex */
4305 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4306 #endif
4307 	 MACRO_LD_PUSH(sUnqlMPGlobal.pDB,pHandle);
4308 	 sUnqlMPGlobal.nDB++;
4309 #if defined(UNQLITE_ENABLE_THREADS)
4310 	/* Leave the global mutex */
4311 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4312 #endif
4313 	/* Set the magic number to identify a valid DB handle */
4314 	 pHandle->nMagic = UNQLITE_DB_MAGIC;
4315 	/* Make the handle available to the caller */
4316 	*ppDB = pHandle;
4317 	return UNQLITE_OK;
4318 Release:
4319 	SyMemBackendRelease(&pHandle->sMem);
4320 	SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pHandle);
4321 	return rc;
4322 }
4323 /*
4324  * [CAPIREF: unqlite_config()]
4325  * Please refer to the official documentation for function purpose and expected parameters.
4326  */
unqlite_config(unqlite * pDb,int nConfigOp,...)4327 int unqlite_config(unqlite *pDb,int nConfigOp,...)
4328 {
4329 	va_list ap;
4330 	int rc;
4331 	if( UNQLITE_DB_MISUSE(pDb) ){
4332 		return UNQLITE_CORRUPT;
4333 	}
4334 #if defined(UNQLITE_ENABLE_THREADS)
4335 	 /* Acquire DB mutex */
4336 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4337 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4338 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
4339 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4340 	 }
4341 #endif
4342 	 va_start(ap, nConfigOp);
4343 	 rc = unqliteConfigure(&(*pDb),nConfigOp, ap);
4344 	 va_end(ap);
4345 #if defined(UNQLITE_ENABLE_THREADS)
4346 	 /* Leave DB mutex */
4347 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4348 #endif
4349 	return rc;
4350 }
4351 /*
4352  * [CAPIREF: unqlite_close()]
4353  * Please refer to the official documentation for function purpose and expected parameters.
4354  */
unqlite_close(unqlite * pDb)4355 int unqlite_close(unqlite *pDb)
4356 {
4357 	int rc;
4358 	if( UNQLITE_DB_MISUSE(pDb) ){
4359 		return UNQLITE_CORRUPT;
4360 	}
4361 #if defined(UNQLITE_ENABLE_THREADS)
4362 	 /* Acquire DB mutex */
4363 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4364 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4365 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
4366 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4367 	 }
4368 #endif
4369 	/* Release the database handle */
4370 	rc = unqliteDbRelease(pDb);
4371 #if defined(UNQLITE_ENABLE_THREADS)
4372 	 /* Leave DB mutex */
4373 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4374 	 /* Release DB mutex */
4375 	 SyMutexRelease(sUnqlMPGlobal.pMutexMethods, pDb->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4376 #endif
4377 #if defined(UNQLITE_ENABLE_THREADS)
4378 	/* Enter the global mutex */
4379 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4380 #endif
4381 	/* Unlink from the list of active database handles */
4382 	 MACRO_LD_REMOVE(sUnqlMPGlobal.pDB, pDb);
4383 	sUnqlMPGlobal.nDB--;
4384 #if defined(UNQLITE_ENABLE_THREADS)
4385 	/* Leave the global mutex */
4386 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
4387 #endif
4388 	/* Release the memory chunk allocated to this handle */
4389 	SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pDb);
4390 	return rc;
4391 }
4392 /*
4393  * [CAPIREF: unqlite_compile()]
4394  * Please refer to the official documentation for function purpose and expected parameters.
4395  */
unqlite_compile(unqlite * pDb,const char * zJx9,int nByte,unqlite_vm ** ppOut)4396 int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut)
4397 {
4398 	jx9_vm *pVm;
4399 	int rc;
4400 	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
4401 		return UNQLITE_CORRUPT;
4402 	}
4403 #if defined(UNQLITE_ENABLE_THREADS)
4404 	 /* Acquire DB mutex */
4405 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4406 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4407 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
4408 			 return UNQLITE_ABORT;
4409 	 }
4410 #endif
4411 	 /* Compile the Jx9 script first */
4412 	 rc = jx9_compile(pDb->sDB.pJx9,zJx9,nByte,&pVm);
4413 	 if( rc == JX9_OK ){
4414 		 /* Allocate a new unqlite VM instance */
4415 		 rc = unqliteInitVm(pDb,pVm,ppOut);
4416 		 if( rc != UNQLITE_OK ){
4417 			 /* Release the Jx9 VM */
4418 			 jx9_vm_release(pVm);
4419 		 }
4420 	 }
4421 #if defined(UNQLITE_ENABLE_THREADS)
4422 	 /* Leave DB mutex */
4423 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4424 #endif
4425 	return rc;
4426 }
4427 /*
4428  * [CAPIREF: unqlite_compile_file()]
4429  * Please refer to the official documentation for function purpose and expected parameters.
4430  */
unqlite_compile_file(unqlite * pDb,const char * zPath,unqlite_vm ** ppOut)4431 int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut)
4432 {
4433 	jx9_vm *pVm;
4434 	int rc;
4435 	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
4436 		return UNQLITE_CORRUPT;
4437 	}
4438 #if defined(UNQLITE_ENABLE_THREADS)
4439 	 /* Acquire DB mutex */
4440 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4441 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4442 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
4443 			 return UNQLITE_ABORT;
4444 	 }
4445 #endif
4446 	 /* Compile the Jx9 script first */
4447 	rc = jx9_compile_file(pDb->sDB.pJx9,zPath,&pVm);
4448 	if( rc == JX9_OK ){
4449 		/* Allocate a new unqlite VM instance */
4450 		rc = unqliteInitVm(pDb,pVm,ppOut);
4451 		if( rc != UNQLITE_OK ){
4452 			/* Release the Jx9 VM */
4453 			jx9_vm_release(pVm);
4454 		}
4455 	}
4456 #if defined(UNQLITE_ENABLE_THREADS)
4457 	 /* Leave DB mutex */
4458 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4459 #endif
4460 	return rc;
4461 }
4462 /*
4463  * Configure an unqlite virtual machine (Mostly Jx9 VM) instance.
4464  */
unqliteVmConfig(unqlite_vm * pVm,sxi32 iOp,va_list ap)4465 static int unqliteVmConfig(unqlite_vm *pVm,sxi32 iOp,va_list ap)
4466 {
4467 	int rc;
4468 	rc = jx9VmConfigure(pVm->pJx9Vm,iOp,ap);
4469 	return rc;
4470 }
4471 /*
4472  * [CAPIREF: unqlite_vm_config()]
4473  * Please refer to the official documentation for function purpose and expected parameters.
4474  */
unqlite_vm_config(unqlite_vm * pVm,int iOp,...)4475 int unqlite_vm_config(unqlite_vm *pVm,int iOp,...)
4476 {
4477 	va_list ap;
4478 	int rc;
4479 	if( UNQLITE_VM_MISUSE(pVm) ){
4480 		return UNQLITE_CORRUPT;
4481 	}
4482 #if defined(UNQLITE_ENABLE_THREADS)
4483 	 /* Acquire VM mutex */
4484 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4485 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4486 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4487 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4488 	 }
4489 #endif
4490 	 va_start(ap,iOp);
4491 	 rc = unqliteVmConfig(pVm,iOp,ap);
4492 	 va_end(ap);
4493 #if defined(UNQLITE_ENABLE_THREADS)
4494 	 /* Leave DB mutex */
4495 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4496 #endif
4497 	return rc;
4498 }
4499 /*
4500  * [CAPIREF: unqlite_vm_exec()]
4501  * Please refer to the official documentation for function purpose and expected parameters.
4502  */
unqlite_vm_exec(unqlite_vm * pVm)4503 int unqlite_vm_exec(unqlite_vm *pVm)
4504 {
4505 	int rc;
4506 	if( UNQLITE_VM_MISUSE(pVm) ){
4507 		return UNQLITE_CORRUPT;
4508 	}
4509 #if defined(UNQLITE_ENABLE_THREADS)
4510 	 /* Acquire VM mutex */
4511 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4512 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4513 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4514 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4515 	 }
4516 #endif
4517 	/* Execute the Jx9 bytecode program */
4518 	 rc = jx9VmByteCodeExec(pVm->pJx9Vm);
4519 #if defined(UNQLITE_ENABLE_THREADS)
4520 	 /* Leave DB mutex */
4521 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4522 #endif
4523 	return rc;
4524 }
4525 /*
4526  * [CAPIREF: unqlite_vm_release()]
4527  * Please refer to the official documentation for function purpose and expected parameters.
4528  */
unqlite_vm_release(unqlite_vm * pVm)4529 int unqlite_vm_release(unqlite_vm *pVm)
4530 {
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 	/* Release the VM */
4544 	 rc = unqliteVmRelease(pVm);
4545 #if defined(UNQLITE_ENABLE_THREADS)
4546 	 /* Leave VM mutex */
4547 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4548 	 /* Release VM mutex */
4549 	 SyMutexRelease(sUnqlMPGlobal.pMutexMethods,pVm->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4550 #endif
4551 	 if( rc == UNQLITE_OK ){
4552 		 unqlite *pDb = pVm->pDb;
4553 		 /* Unlink from the list of active VM's */
4554 #if defined(UNQLITE_ENABLE_THREADS)
4555 			/* Acquire DB mutex */
4556 			SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4557 			if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4558 				UNQLITE_THRD_DB_RELEASE(pDb) ){
4559 					return UNQLITE_ABORT; /* Another thread have released this instance */
4560 			}
4561 #endif
4562 		MACRO_LD_REMOVE(pDb->pVms, pVm);
4563 		pDb->iVm--;
4564 		/* Release the memory chunk allocated to this instance */
4565 		SyMemBackendPoolFree(&pDb->sMem,pVm);
4566 #if defined(UNQLITE_ENABLE_THREADS)
4567 			/* Leave DB mutex */
4568 			SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4569 #endif
4570 	 }
4571 	 return rc;
4572 }
4573 /*
4574  * [CAPIREF: unqlite_vm_reset()]
4575  * Please refer to the official documentation for function purpose and expected parameters.
4576  */
unqlite_vm_reset(unqlite_vm * pVm)4577 int unqlite_vm_reset(unqlite_vm *pVm)
4578 {
4579 	int rc;
4580 	if( UNQLITE_VM_MISUSE(pVm) ){
4581 		return UNQLITE_CORRUPT;
4582 	}
4583 #if defined(UNQLITE_ENABLE_THREADS)
4584 	 /* Acquire VM mutex */
4585 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4586 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4587 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4588 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4589 	 }
4590 #endif
4591 	/* Reset the Jx9 VM */
4592 	 rc = jx9VmReset(pVm->pJx9Vm);
4593 #if defined(UNQLITE_ENABLE_THREADS)
4594 	 /* Leave DB mutex */
4595 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4596 #endif
4597 	return rc;
4598 }
4599 /*
4600  * [CAPIREF: unqlite_vm_dump()]
4601  * Please refer to the official documentation for function purpose and expected parameters.
4602  */
unqlite_vm_dump(unqlite_vm * pVm,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)4603 int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData)
4604 {
4605 	int rc;
4606 	if( UNQLITE_VM_MISUSE(pVm) ){
4607 		return UNQLITE_CORRUPT;
4608 	}
4609 #if defined(UNQLITE_ENABLE_THREADS)
4610 	 /* Acquire VM mutex */
4611 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4612 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4613 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4614 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4615 	 }
4616 #endif
4617 	/* Dump the Jx9 VM */
4618 	 rc = jx9VmDump(pVm->pJx9Vm,xConsumer,pUserData);
4619 #if defined(UNQLITE_ENABLE_THREADS)
4620 	 /* Leave DB mutex */
4621 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4622 #endif
4623 	return rc;
4624 }
4625 /*
4626  * [CAPIREF: unqlite_vm_extract_variable()]
4627  * Please refer to the official documentation for function purpose and expected parameters.
4628  */
unqlite_vm_extract_variable(unqlite_vm * pVm,const char * zVarname)4629 unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname)
4630 {
4631 	unqlite_value *pValue;
4632 	SyString sVariable;
4633 	if( UNQLITE_VM_MISUSE(pVm) ){
4634 		return 0;
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 0; /* Another thread have released this instance */
4642 	 }
4643 #endif
4644 	 /* Extract the target variable */
4645 	SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname));
4646 	pValue = jx9VmExtractVariable(pVm->pJx9Vm,&sVariable);
4647 #if defined(UNQLITE_ENABLE_THREADS)
4648 	 /* Leave DB mutex */
4649 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4650 #endif
4651 	return pValue;
4652 }
4653 /*
4654  * [CAPIREF: unqlite_create_function()]
4655  * Please refer to the official documentation for function purpose and expected parameters.
4656  */
unqlite_create_function(unqlite_vm * pVm,const char * zName,int (* xFunc)(unqlite_context *,int,unqlite_value **),void * pUserData)4657 int unqlite_create_function(unqlite_vm *pVm, const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData)
4658 {
4659 	SyString sName;
4660 	int rc;
4661 	if( UNQLITE_VM_MISUSE(pVm) ){
4662 		return UNQLITE_CORRUPT;
4663 	}
4664 	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
4665 	/* Remove leading and trailing white spaces */
4666 	SyStringFullTrim(&sName);
4667 	/* Ticket 1433-003: NULL values are not allowed */
4668 	if( sName.nByte < 1 || xFunc == 0 ){
4669 		return UNQLITE_INVALID;
4670 	}
4671 #if defined(UNQLITE_ENABLE_THREADS)
4672 	 /* Acquire VM mutex */
4673 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4674 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4675 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4676 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4677 	 }
4678 #endif
4679 	 /* Install the foreign function */
4680 	 rc = jx9VmInstallForeignFunction(pVm->pJx9Vm,&sName,xFunc,pUserData);
4681 #if defined(UNQLITE_ENABLE_THREADS)
4682 	 /* Leave DB mutex */
4683 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4684 #endif
4685 	return rc;
4686 }
4687 /*
4688  * [CAPIREF: unqlite_delete_function()]
4689  * Please refer to the official documentation for function purpose and expected parameters.
4690  */
unqlite_delete_function(unqlite_vm * pVm,const char * zName)4691 int unqlite_delete_function(unqlite_vm *pVm, const char *zName)
4692 {
4693 	int rc;
4694 	if( UNQLITE_VM_MISUSE(pVm) ){
4695 		return UNQLITE_CORRUPT;
4696 	}
4697 #if defined(UNQLITE_ENABLE_THREADS)
4698 	 /* Acquire VM mutex */
4699 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4700 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4701 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4702 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4703 	 }
4704 #endif
4705 	 /* Unlink the foreign function */
4706 	 rc = jx9DeleteFunction(pVm->pJx9Vm,zName);
4707 #if defined(UNQLITE_ENABLE_THREADS)
4708 	 /* Leave DB mutex */
4709 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4710 #endif
4711 	return rc;
4712 }
4713 /*
4714  * [CAPIREF: unqlite_create_constant()]
4715  * Please refer to the official documentation for function purpose and expected parameters.
4716  */
unqlite_create_constant(unqlite_vm * pVm,const char * zName,void (* xExpand)(unqlite_value *,void *),void * pUserData)4717 int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData)
4718 {
4719 	SyString sName;
4720 	int rc;
4721 	if( UNQLITE_VM_MISUSE(pVm) ){
4722 		return UNQLITE_CORRUPT;
4723 	}
4724 	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
4725 	/* Remove leading and trailing white spaces */
4726 	SyStringFullTrim(&sName);
4727 	if( sName.nByte < 1 ){
4728 		/* Empty constant name */
4729 		return UNQLITE_INVALID;
4730 	}
4731 	/* TICKET 1433-003: NULL pointer is harmless operation */
4732 	if( xExpand == 0 ){
4733 		return UNQLITE_INVALID;
4734 	}
4735 #if defined(UNQLITE_ENABLE_THREADS)
4736 	 /* Acquire VM mutex */
4737 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4738 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4739 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4740 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4741 	 }
4742 #endif
4743 	 /* Install the foreign constant */
4744 	 rc = jx9VmRegisterConstant(pVm->pJx9Vm,&sName,xExpand,pUserData);
4745 #if defined(UNQLITE_ENABLE_THREADS)
4746 	 /* Leave DB mutex */
4747 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4748 #endif
4749 	return rc;
4750 }
4751 /*
4752  * [CAPIREF: unqlite_delete_constant()]
4753  * Please refer to the official documentation for function purpose and expected parameters.
4754  */
unqlite_delete_constant(unqlite_vm * pVm,const char * zName)4755 int unqlite_delete_constant(unqlite_vm *pVm, const char *zName)
4756 {
4757 	int rc;
4758 	if( UNQLITE_VM_MISUSE(pVm) ){
4759 		return UNQLITE_CORRUPT;
4760 	}
4761 #if defined(UNQLITE_ENABLE_THREADS)
4762 	 /* Acquire VM mutex */
4763 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4764 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
4765 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
4766 			 return UNQLITE_ABORT; /* Another thread have released this instance */
4767 	 }
4768 #endif
4769 	 /* Unlink the foreign constant */
4770 	 rc = Jx9DeleteConstant(pVm->pJx9Vm,zName);
4771 #if defined(UNQLITE_ENABLE_THREADS)
4772 	 /* Leave DB mutex */
4773 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
4774 #endif
4775 	return rc;
4776 }
4777 /*
4778  * [CAPIREF: unqlite_value_int()]
4779  * Please refer to the official documentation for function purpose and expected parameters.
4780  */
unqlite_value_int(unqlite_value * pVal,int iValue)4781 int unqlite_value_int(unqlite_value *pVal, int iValue)
4782 {
4783 	return jx9_value_int(pVal,iValue);
4784 }
4785 /*
4786  * [CAPIREF: unqlite_value_int64()]
4787  * Please refer to the official documentation for function purpose and expected parameters.
4788  */
unqlite_value_int64(unqlite_value * pVal,unqlite_int64 iValue)4789 int unqlite_value_int64(unqlite_value *pVal,unqlite_int64 iValue)
4790 {
4791 	return jx9_value_int64(pVal,iValue);
4792 }
4793 /*
4794  * [CAPIREF: unqlite_value_bool()]
4795  * Please refer to the official documentation for function purpose and expected parameters.
4796  */
unqlite_value_bool(unqlite_value * pVal,int iBool)4797 int unqlite_value_bool(unqlite_value *pVal, int iBool)
4798 {
4799 	return jx9_value_bool(pVal,iBool);
4800 }
4801 /*
4802  * [CAPIREF: unqlite_value_null()]
4803  * Please refer to the official documentation for function purpose and expected parameters.
4804  */
unqlite_value_null(unqlite_value * pVal)4805 int unqlite_value_null(unqlite_value *pVal)
4806 {
4807 	return jx9_value_null(pVal);
4808 }
4809 /*
4810  * [CAPIREF: unqlite_value_double()]
4811  * Please refer to the official documentation for function purpose and expected parameters.
4812  */
unqlite_value_double(unqlite_value * pVal,double Value)4813 int unqlite_value_double(unqlite_value *pVal, double Value)
4814 {
4815 	return jx9_value_double(pVal,Value);
4816 }
4817 /*
4818  * [CAPIREF: unqlite_value_string()]
4819  * Please refer to the official documentation for function purpose and expected parameters.
4820  */
unqlite_value_string(unqlite_value * pVal,const char * zString,int nLen)4821 int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen)
4822 {
4823 	return jx9_value_string(pVal,zString,nLen);
4824 }
4825 /*
4826  * [CAPIREF: unqlite_value_string_format()]
4827  * Please refer to the official documentation for function purpose and expected parameters.
4828  */
unqlite_value_string_format(unqlite_value * pVal,const char * zFormat,...)4829 int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...)
4830 {
4831 	va_list ap;
4832 	int rc;
4833 	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
4834 		/* Invalidate any prior representation */
4835 		jx9MemObjRelease(pVal);
4836 		MemObjSetType(pVal, MEMOBJ_STRING);
4837 	}
4838 	va_start(ap, zFormat);
4839 	rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
4840 	va_end(ap);
4841 	return UNQLITE_OK;
4842 }
4843 /*
4844  * [CAPIREF: unqlite_value_reset_string_cursor()]
4845  * Please refer to the official documentation for function purpose and expected parameters.
4846  */
unqlite_value_reset_string_cursor(unqlite_value * pVal)4847 int unqlite_value_reset_string_cursor(unqlite_value *pVal)
4848 {
4849 	return jx9_value_reset_string_cursor(pVal);
4850 }
4851 /*
4852  * [CAPIREF: unqlite_value_resource()]
4853  * Please refer to the official documentation for function purpose and expected parameters.
4854  */
unqlite_value_resource(unqlite_value * pVal,void * pUserData)4855 int unqlite_value_resource(unqlite_value *pVal,void *pUserData)
4856 {
4857 	return jx9_value_resource(pVal,pUserData);
4858 }
4859 /*
4860  * [CAPIREF: unqlite_value_release()]
4861  * Please refer to the official documentation for function purpose and expected parameters.
4862  */
unqlite_value_release(unqlite_value * pVal)4863 int unqlite_value_release(unqlite_value *pVal)
4864 {
4865 	return jx9_value_release(pVal);
4866 }
4867 /*
4868  * [CAPIREF: unqlite_value_to_int()]
4869  * Please refer to the official documentation for function purpose and expected parameters.
4870  */
unqlite_value_to_int(unqlite_value * pValue)4871 int unqlite_value_to_int(unqlite_value *pValue)
4872 {
4873 	return jx9_value_to_int(pValue);
4874 }
4875 /*
4876  * [CAPIREF: unqlite_value_to_bool()]
4877  * Please refer to the official documentation for function purpose and expected parameters.
4878  */
unqlite_value_to_bool(unqlite_value * pValue)4879 int unqlite_value_to_bool(unqlite_value *pValue)
4880 {
4881 	return jx9_value_to_bool(pValue);
4882 }
4883 /*
4884  * [CAPIREF: unqlite_value_to_int64()]
4885  * Please refer to the official documentation for function purpose and expected parameters.
4886  */
unqlite_value_to_int64(unqlite_value * pValue)4887 unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue)
4888 {
4889 	return jx9_value_to_int64(pValue);
4890 }
4891 /*
4892  * [CAPIREF: unqlite_value_to_double()]
4893  * Please refer to the official documentation for function purpose and expected parameters.
4894  */
unqlite_value_to_double(unqlite_value * pValue)4895 double unqlite_value_to_double(unqlite_value *pValue)
4896 {
4897 	return jx9_value_to_double(pValue);
4898 }
4899 /*
4900  * [CAPIREF: unqlite_value_to_string()]
4901  * Please refer to the official documentation for function purpose and expected parameters.
4902  */
unqlite_value_to_string(unqlite_value * pValue,int * pLen)4903 const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen)
4904 {
4905 	return jx9_value_to_string(pValue,pLen);
4906 }
4907 /*
4908  * [CAPIREF: unqlite_value_to_resource()]
4909  * Please refer to the official documentation for function purpose and expected parameters.
4910  */
unqlite_value_to_resource(unqlite_value * pValue)4911 void * unqlite_value_to_resource(unqlite_value *pValue)
4912 {
4913 	return jx9_value_to_resource(pValue);
4914 }
4915 /*
4916  * [CAPIREF: unqlite_value_compare()]
4917  * Please refer to the official documentation for function purpose and expected parameters.
4918  */
unqlite_value_compare(unqlite_value * pLeft,unqlite_value * pRight,int bStrict)4919 int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict)
4920 {
4921 	return jx9_value_compare(pLeft,pRight,bStrict);
4922 }
4923 /*
4924  * [CAPIREF: unqlite_result_int()]
4925  * Please refer to the official documentation for function purpose and expected parameters.
4926  */
unqlite_result_int(unqlite_context * pCtx,int iValue)4927 int unqlite_result_int(unqlite_context *pCtx, int iValue)
4928 {
4929 	return jx9_result_int(pCtx,iValue);
4930 }
4931 /*
4932  * [CAPIREF: unqlite_result_int64()]
4933  * Please refer to the official documentation for function purpose and expected parameters.
4934  */
unqlite_result_int64(unqlite_context * pCtx,unqlite_int64 iValue)4935 int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue)
4936 {
4937 	return jx9_result_int64(pCtx,iValue);
4938 }
4939 /*
4940  * [CAPIREF: unqlite_result_bool()]
4941  * Please refer to the official documentation for function purpose and expected parameters.
4942  */
unqlite_result_bool(unqlite_context * pCtx,int iBool)4943 int unqlite_result_bool(unqlite_context *pCtx, int iBool)
4944 {
4945 	return jx9_result_bool(pCtx,iBool);
4946 }
4947 /*
4948  * [CAPIREF: unqlite_result_double()]
4949  * Please refer to the official documentation for function purpose and expected parameters.
4950  */
unqlite_result_double(unqlite_context * pCtx,double Value)4951 int unqlite_result_double(unqlite_context *pCtx, double Value)
4952 {
4953 	return jx9_result_double(pCtx,Value);
4954 }
4955 /*
4956  * [CAPIREF: unqlite_result_null()]
4957  * Please refer to the official documentation for function purpose and expected parameters.
4958  */
unqlite_result_null(unqlite_context * pCtx)4959 int unqlite_result_null(unqlite_context *pCtx)
4960 {
4961 	return jx9_result_null(pCtx);
4962 }
4963 /*
4964  * [CAPIREF: unqlite_result_string()]
4965  * Please refer to the official documentation for function purpose and expected parameters.
4966  */
unqlite_result_string(unqlite_context * pCtx,const char * zString,int nLen)4967 int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen)
4968 {
4969 	return jx9_result_string(pCtx,zString,nLen);
4970 }
4971 /*
4972  * [CAPIREF: unqlite_result_string_format()]
4973  * Please refer to the official documentation for function purpose and expected parameters.
4974  */
unqlite_result_string_format(unqlite_context * pCtx,const char * zFormat,...)4975 int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...)
4976 {
4977 	jx9_value *p;
4978 	va_list ap;
4979 	int rc;
4980 	p = pCtx->pRet;
4981 	if( (p->iFlags & MEMOBJ_STRING) == 0 ){
4982 		/* Invalidate any prior representation */
4983 		jx9MemObjRelease(p);
4984 		MemObjSetType(p, MEMOBJ_STRING);
4985 	}
4986 	/* Format the given string */
4987 	va_start(ap, zFormat);
4988 	rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
4989 	va_end(ap);
4990 	return rc;
4991 }
4992 /*
4993  * [CAPIREF: unqlite_result_value()]
4994  * Please refer to the official documentation for function purpose and expected parameters.
4995  */
unqlite_result_value(unqlite_context * pCtx,unqlite_value * pValue)4996 int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue)
4997 {
4998 	return jx9_result_value(pCtx,pValue);
4999 }
5000 /*
5001  * [CAPIREF: unqlite_result_resource()]
5002  * Please refer to the official documentation for function purpose and expected parameters.
5003  */
unqlite_result_resource(unqlite_context * pCtx,void * pUserData)5004 int unqlite_result_resource(unqlite_context *pCtx, void *pUserData)
5005 {
5006 	return jx9_result_resource(pCtx,pUserData);
5007 }
5008 /*
5009  * [CAPIREF: unqlite_value_is_int()]
5010  * Please refer to the official documentation for function purpose and expected parameters.
5011  */
unqlite_value_is_int(unqlite_value * pVal)5012 int unqlite_value_is_int(unqlite_value *pVal)
5013 {
5014 	return jx9_value_is_int(pVal);
5015 }
5016 /*
5017  * [CAPIREF: unqlite_value_is_float()]
5018  * Please refer to the official documentation for function purpose and expected parameters.
5019  */
unqlite_value_is_float(unqlite_value * pVal)5020 int unqlite_value_is_float(unqlite_value *pVal)
5021 {
5022 	return jx9_value_is_float(pVal);
5023 }
5024 /*
5025  * [CAPIREF: unqlite_value_is_bool()]
5026  * Please refer to the official documentation for function purpose and expected parameters.
5027  */
unqlite_value_is_bool(unqlite_value * pVal)5028 int unqlite_value_is_bool(unqlite_value *pVal)
5029 {
5030 	return jx9_value_is_bool(pVal);
5031 }
5032 /*
5033  * [CAPIREF: unqlite_value_is_string()]
5034  * Please refer to the official documentation for function purpose and expected parameters.
5035  */
unqlite_value_is_string(unqlite_value * pVal)5036 int unqlite_value_is_string(unqlite_value *pVal)
5037 {
5038 	return jx9_value_is_string(pVal);
5039 }
5040 /*
5041  * [CAPIREF: unqlite_value_is_null()]
5042  * Please refer to the official documentation for function purpose and expected parameters.
5043  */
unqlite_value_is_null(unqlite_value * pVal)5044 int unqlite_value_is_null(unqlite_value *pVal)
5045 {
5046 	return jx9_value_is_null(pVal);
5047 }
5048 /*
5049  * [CAPIREF: unqlite_value_is_numeric()]
5050  * Please refer to the official documentation for function purpose and expected parameters.
5051  */
unqlite_value_is_numeric(unqlite_value * pVal)5052 int unqlite_value_is_numeric(unqlite_value *pVal)
5053 {
5054 	return jx9_value_is_numeric(pVal);
5055 }
5056 /*
5057  * [CAPIREF: unqlite_value_is_callable()]
5058  * Please refer to the official documentation for function purpose and expected parameters.
5059  */
unqlite_value_is_callable(unqlite_value * pVal)5060 int unqlite_value_is_callable(unqlite_value *pVal)
5061 {
5062 	return jx9_value_is_callable(pVal);
5063 }
5064 /*
5065  * [CAPIREF: unqlite_value_is_scalar()]
5066  * Please refer to the official documentation for function purpose and expected parameters.
5067  */
unqlite_value_is_scalar(unqlite_value * pVal)5068 int unqlite_value_is_scalar(unqlite_value *pVal)
5069 {
5070 	return jx9_value_is_scalar(pVal);
5071 }
5072 /*
5073  * [CAPIREF: unqlite_value_is_json_array()]
5074  * Please refer to the official documentation for function purpose and expected parameters.
5075  */
unqlite_value_is_json_array(unqlite_value * pVal)5076 int unqlite_value_is_json_array(unqlite_value *pVal)
5077 {
5078 	return jx9_value_is_json_array(pVal);
5079 }
5080 /*
5081  * [CAPIREF: unqlite_value_is_json_object()]
5082  * Please refer to the official documentation for function purpose and expected parameters.
5083  */
unqlite_value_is_json_object(unqlite_value * pVal)5084 int unqlite_value_is_json_object(unqlite_value *pVal)
5085 {
5086 	return jx9_value_is_json_object(pVal);
5087 }
5088 /*
5089  * [CAPIREF: unqlite_value_is_resource()]
5090  * Please refer to the official documentation for function purpose and expected parameters.
5091  */
unqlite_value_is_resource(unqlite_value * pVal)5092 int unqlite_value_is_resource(unqlite_value *pVal)
5093 {
5094 	return jx9_value_is_resource(pVal);
5095 }
5096 /*
5097  * [CAPIREF: unqlite_value_is_empty()]
5098  * Please refer to the official documentation for function purpose and expected parameters.
5099  */
unqlite_value_is_empty(unqlite_value * pVal)5100 int unqlite_value_is_empty(unqlite_value *pVal)
5101 {
5102 	return jx9_value_is_empty(pVal);
5103 }
5104 /*
5105  * [CAPIREF: unqlite_array_fetch()]
5106  * Please refer to the official documentation for function purpose and expected parameters.
5107  */
unqlite_array_fetch(unqlite_value * pArray,const char * zKey,int nByte)5108 unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte)
5109 {
5110 	return jx9_array_fetch(pArray,zKey,nByte);
5111 }
5112 /*
5113  * [CAPIREF: unqlite_array_walk()]
5114  * Please refer to the official documentation for function purpose and expected parameters.
5115  */
unqlite_array_walk(unqlite_value * pArray,int (* xWalk)(unqlite_value *,unqlite_value *,void *),void * pUserData)5116 int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData)
5117 {
5118 	return jx9_array_walk(pArray,xWalk,pUserData);
5119 }
5120 /*
5121  * [CAPIREF: unqlite_array_add_elem()]
5122  * Please refer to the official documentation for function purpose and expected parameters.
5123  */
unqlite_array_add_elem(unqlite_value * pArray,unqlite_value * pKey,unqlite_value * pValue)5124 int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue)
5125 {
5126 	return jx9_array_add_elem(pArray,pKey,pValue);
5127 }
5128 /*
5129  * [CAPIREF: unqlite_array_add_strkey_elem()]
5130  * Please refer to the official documentation for function purpose and expected parameters.
5131  */
unqlite_array_add_strkey_elem(unqlite_value * pArray,const char * zKey,unqlite_value * pValue)5132 int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue)
5133 {
5134 	return jx9_array_add_strkey_elem(pArray,zKey,pValue);
5135 }
5136 /*
5137  * [CAPIREF: unqlite_array_count()]
5138  * Please refer to the official documentation for function purpose and expected parameters.
5139  */
unqlite_array_count(unqlite_value * pArray)5140 int unqlite_array_count(unqlite_value *pArray)
5141 {
5142 	return (int)jx9_array_count(pArray);
5143 }
5144 /*
5145  * [CAPIREF: unqlite_vm_new_scalar()]
5146  * Please refer to the official documentation for function purpose and expected parameters.
5147  */
unqlite_vm_new_scalar(unqlite_vm * pVm)5148 unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm)
5149 {
5150 	unqlite_value *pValue;
5151 	if( UNQLITE_VM_MISUSE(pVm) ){
5152 		return 0;
5153 	}
5154 #if defined(UNQLITE_ENABLE_THREADS)
5155 	 /* Acquire VM mutex */
5156 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5157 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5158 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
5159 			 return 0; /* Another thread have released this instance */
5160 	 }
5161 #endif
5162 	 pValue = jx9_new_scalar(pVm->pJx9Vm);
5163 #if defined(UNQLITE_ENABLE_THREADS)
5164 	 /* Leave DB mutex */
5165 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5166 #endif
5167 	return pValue;
5168 }
5169 /*
5170  * [CAPIREF: unqlite_vm_new_array()]
5171  * Please refer to the official documentation for function purpose and expected parameters.
5172  */
unqlite_vm_new_array(unqlite_vm * pVm)5173 unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm)
5174 {
5175 	unqlite_value *pValue;
5176 	if( UNQLITE_VM_MISUSE(pVm) ){
5177 		return 0;
5178 	}
5179 #if defined(UNQLITE_ENABLE_THREADS)
5180 	 /* Acquire VM mutex */
5181 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5182 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5183 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
5184 			 return 0; /* Another thread have released this instance */
5185 	 }
5186 #endif
5187 	 pValue = jx9_new_array(pVm->pJx9Vm);
5188 #if defined(UNQLITE_ENABLE_THREADS)
5189 	 /* Leave DB mutex */
5190 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5191 #endif
5192 	return pValue;
5193 }
5194 /*
5195  * [CAPIREF: unqlite_vm_release_value()]
5196  * Please refer to the official documentation for function purpose and expected parameters.
5197  */
unqlite_vm_release_value(unqlite_vm * pVm,unqlite_value * pValue)5198 int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue)
5199 {
5200 	int rc;
5201 	if( UNQLITE_VM_MISUSE(pVm) ){
5202 		return UNQLITE_CORRUPT;
5203 	}
5204 #if defined(UNQLITE_ENABLE_THREADS)
5205 	 /* Acquire VM mutex */
5206 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5207 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5208 		 UNQLITE_THRD_VM_RELEASE(pVm) ){
5209 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5210 	 }
5211 #endif
5212 	 rc = jx9_release_value(pVm->pJx9Vm,pValue);
5213 #if defined(UNQLITE_ENABLE_THREADS)
5214 	 /* Leave DB mutex */
5215 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5216 #endif
5217 	return rc;
5218 }
5219 /*
5220  * [CAPIREF: unqlite_context_output()]
5221  * Please refer to the official documentation for function purpose and expected parameters.
5222  */
unqlite_context_output(unqlite_context * pCtx,const char * zString,int nLen)5223 int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen)
5224 {
5225 	return jx9_context_output(pCtx,zString,nLen);
5226 }
5227 /*
5228  * [CAPIREF: unqlite_context_output_format()]
5229  * Please refer to the official documentation for function purpose and expected parameters.
5230  */
unqlite_context_output_format(unqlite_context * pCtx,const char * zFormat,...)5231 int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...)
5232 {
5233 	va_list ap;
5234 	int rc;
5235 	va_start(ap, zFormat);
5236 	rc = jx9VmOutputConsumeAp(pCtx->pVm,zFormat, ap);
5237 	va_end(ap);
5238 	return rc;
5239 }
5240 /*
5241  * [CAPIREF: unqlite_context_throw_error()]
5242  * Please refer to the official documentation for function purpose and expected parameters.
5243  */
unqlite_context_throw_error(unqlite_context * pCtx,int iErr,const char * zErr)5244 int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr)
5245 {
5246 	return jx9_context_throw_error(pCtx,iErr,zErr);
5247 }
5248 /*
5249  * [CAPIREF: unqlite_context_throw_error_format()]
5250  * Please refer to the official documentation for function purpose and expected parameters.
5251  */
unqlite_context_throw_error_format(unqlite_context * pCtx,int iErr,const char * zFormat,...)5252 int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...)
5253 {
5254 	va_list ap;
5255 	int rc;
5256 	if( zFormat == 0){
5257 		return JX9_OK;
5258 	}
5259 	va_start(ap, zFormat);
5260 	rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
5261 	va_end(ap);
5262 	return rc;
5263 }
5264 /*
5265  * [CAPIREF: unqlite_context_random_num()]
5266  * Please refer to the official documentation for function purpose and expected parameters.
5267  */
unqlite_context_random_num(unqlite_context * pCtx)5268 unsigned int unqlite_context_random_num(unqlite_context *pCtx)
5269 {
5270 	return jx9_context_random_num(pCtx);
5271 }
5272 /*
5273  * [CAPIREF: unqlite_context_random_string()]
5274  * Please refer to the official documentation for function purpose and expected parameters.
5275  */
unqlite_context_random_string(unqlite_context * pCtx,char * zBuf,int nBuflen)5276 int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen)
5277 {
5278 	return jx9_context_random_string(pCtx,zBuf,nBuflen);
5279 }
5280 /*
5281  * [CAPIREF: unqlite_context_user_data()]
5282  * Please refer to the official documentation for function purpose and expected parameters.
5283  */
unqlite_context_user_data(unqlite_context * pCtx)5284 void * unqlite_context_user_data(unqlite_context *pCtx)
5285 {
5286 	return jx9_context_user_data(pCtx);
5287 }
5288 /*
5289  * [CAPIREF: unqlite_context_push_aux_data()]
5290  * Please refer to the official documentation for function purpose and expected parameters.
5291  */
unqlite_context_push_aux_data(unqlite_context * pCtx,void * pUserData)5292 int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData)
5293 {
5294 	return jx9_context_push_aux_data(pCtx,pUserData);
5295 }
5296 /*
5297  * [CAPIREF: unqlite_context_peek_aux_data()]
5298  * Please refer to the official documentation for function purpose and expected parameters.
5299  */
unqlite_context_peek_aux_data(unqlite_context * pCtx)5300 void * unqlite_context_peek_aux_data(unqlite_context *pCtx)
5301 {
5302 	return jx9_context_peek_aux_data(pCtx);
5303 }
5304 /*
5305  * [CAPIREF: unqlite_context_pop_aux_data()]
5306  * Please refer to the official documentation for function purpose and expected parameters.
5307  */
unqlite_context_pop_aux_data(unqlite_context * pCtx)5308 void * unqlite_context_pop_aux_data(unqlite_context *pCtx)
5309 {
5310 	return jx9_context_pop_aux_data(pCtx);
5311 }
5312 /*
5313  * [CAPIREF: unqlite_context_result_buf_length()]
5314  * Please refer to the official documentation for function purpose and expected parameters.
5315  */
unqlite_context_result_buf_length(unqlite_context * pCtx)5316 unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx)
5317 {
5318 	return jx9_context_result_buf_length(pCtx);
5319 }
5320 /*
5321  * [CAPIREF: unqlite_function_name()]
5322  * Please refer to the official documentation for function purpose and expected parameters.
5323  */
unqlite_function_name(unqlite_context * pCtx)5324 const char * unqlite_function_name(unqlite_context *pCtx)
5325 {
5326 	return jx9_function_name(pCtx);
5327 }
5328 /*
5329  * [CAPIREF: unqlite_context_new_scalar()]
5330  * Please refer to the official documentation for function purpose and expected parameters.
5331  */
unqlite_context_new_scalar(unqlite_context * pCtx)5332 unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx)
5333 {
5334 	return jx9_context_new_scalar(pCtx);
5335 }
5336 /*
5337  * [CAPIREF: unqlite_context_new_array()]
5338  * Please refer to the official documentation for function purpose and expected parameters.
5339  */
unqlite_context_new_array(unqlite_context * pCtx)5340 unqlite_value * unqlite_context_new_array(unqlite_context *pCtx)
5341 {
5342 	return jx9_context_new_array(pCtx);
5343 }
5344 /*
5345  * [CAPIREF: unqlite_context_release_value()]
5346  * Please refer to the official documentation for function purpose and expected parameters.
5347  */
unqlite_context_release_value(unqlite_context * pCtx,unqlite_value * pValue)5348 void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue)
5349 {
5350 	jx9_context_release_value(pCtx,pValue);
5351 }
5352 /*
5353  * [CAPIREF: unqlite_context_alloc_chunk()]
5354  * Please refer to the official documentation for function purpose and expected parameters.
5355  */
unqlite_context_alloc_chunk(unqlite_context * pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)5356 void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)
5357 {
5358 	return jx9_context_alloc_chunk(pCtx,nByte,ZeroChunk,AutoRelease);
5359 }
5360 /*
5361  * [CAPIREF: unqlite_context_realloc_chunk()]
5362  * Please refer to the official documentation for function purpose and expected parameters.
5363  */
unqlite_context_realloc_chunk(unqlite_context * pCtx,void * pChunk,unsigned int nByte)5364 void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte)
5365 {
5366 	return jx9_context_realloc_chunk(pCtx,pChunk,nByte);
5367 }
5368 /*
5369  * [CAPIREF: unqlite_context_free_chunk()]
5370  * Please refer to the official documentation for function purpose and expected parameters.
5371  */
unqlite_context_free_chunk(unqlite_context * pCtx,void * pChunk)5372 void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk)
5373 {
5374 	jx9_context_free_chunk(pCtx,pChunk);
5375 }
5376 /*
5377  * [CAPIREF: unqlite_kv_store()]
5378  * Please refer to the official documentation for function purpose and expected parameters.
5379  */
unqlite_kv_store(unqlite * pDb,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)5380 int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
5381 {
5382 	unqlite_kv_engine *pEngine;
5383 	int rc;
5384 	if( UNQLITE_DB_MISUSE(pDb) ){
5385 		return UNQLITE_CORRUPT;
5386 	}
5387 #if defined(UNQLITE_ENABLE_THREADS)
5388 	 /* Acquire DB mutex */
5389 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5390 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5391 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5392 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5393 	 }
5394 #endif
5395 	 /* Point to the underlying storage engine */
5396 	 pEngine = unqlitePagerGetKvEngine(pDb);
5397 	 if( pEngine->pIo->pMethods->xReplace == 0 ){
5398 		 /* Storage engine does not implement such method */
5399 		 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
5400 		 rc = UNQLITE_NOTIMPLEMENTED;
5401 	 }else{
5402 		 if( nKeyLen < 0 ){
5403 			 /* Assume a null terminated string and compute it's length */
5404 			 nKeyLen = SyStrlen((const char *)pKey);
5405 		 }
5406 		 if( !nKeyLen ){
5407 			 unqliteGenError(pDb,"Empty key");
5408 			 rc = UNQLITE_EMPTY;
5409 		 }else{
5410 			 /* Perform the requested operation */
5411 			 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,pData,nDataLen);
5412 		 }
5413 	 }
5414 #if defined(UNQLITE_ENABLE_THREADS)
5415 	 /* Leave DB mutex */
5416 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5417 #endif
5418 	return rc;
5419 }
5420 /*
5421  * [CAPIREF: unqlite_kv_store_fmt()]
5422  * Please refer to the official documentation for function purpose and expected parameters.
5423  */
unqlite_kv_store_fmt(unqlite * pDb,const void * pKey,int nKeyLen,const char * zFormat,...)5424 int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
5425 {
5426 	unqlite_kv_engine *pEngine;
5427 	int rc;
5428 	if( UNQLITE_DB_MISUSE(pDb) ){
5429 		return UNQLITE_CORRUPT;
5430 	}
5431 #if defined(UNQLITE_ENABLE_THREADS)
5432 	 /* Acquire DB mutex */
5433 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5434 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5435 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5436 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5437 	 }
5438 #endif
5439 	 /* Point to the underlying storage engine */
5440 	 pEngine = unqlitePagerGetKvEngine(pDb);
5441 	 if( pEngine->pIo->pMethods->xReplace == 0 ){
5442 		 /* Storage engine does not implement such method */
5443 		 unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
5444 		 rc = UNQLITE_NOTIMPLEMENTED;
5445 	 }else{
5446 		 if( nKeyLen < 0 ){
5447 			 /* Assume a null terminated string and compute it's length */
5448 			 nKeyLen = SyStrlen((const char *)pKey);
5449 		 }
5450 		 if( !nKeyLen ){
5451 			 unqliteGenError(pDb,"Empty key");
5452 			 rc = UNQLITE_EMPTY;
5453 		 }else{
5454 			 SyBlob sWorker; /* Working buffer */
5455 			 va_list ap;
5456 			 SyBlobInit(&sWorker,&pDb->sMem);
5457 			 /* Format the data */
5458 			 va_start(ap,zFormat);
5459 			 SyBlobFormatAp(&sWorker,zFormat,ap);
5460 			 va_end(ap);
5461 			 /* Perform the requested operation */
5462 			 rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
5463 			 /* Clean up */
5464 			 SyBlobRelease(&sWorker);
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_append()]
5475  * Please refer to the official documentation for function purpose and expected parameters.
5476  */
unqlite_kv_append(unqlite * pDb,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)5477 int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
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->xAppend == 0 ){
5495 		 /* Storage engine does not implement such method */
5496 		 unqliteGenError(pDb,"xAppend() 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 			 /* Perform the requested operation */
5508 			 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,pData,nDataLen);
5509 		 }
5510 	 }
5511 #if defined(UNQLITE_ENABLE_THREADS)
5512 	 /* Leave DB mutex */
5513 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5514 #endif
5515 	return rc;
5516 }
5517 /*
5518  * [CAPIREF: unqlite_kv_append_fmt()]
5519  * Please refer to the official documentation for function purpose and expected parameters.
5520  */
unqlite_kv_append_fmt(unqlite * pDb,const void * pKey,int nKeyLen,const char * zFormat,...)5521 int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
5522 {
5523 	unqlite_kv_engine *pEngine;
5524 	int rc;
5525 	if( UNQLITE_DB_MISUSE(pDb) ){
5526 		return UNQLITE_CORRUPT;
5527 	}
5528 #if defined(UNQLITE_ENABLE_THREADS)
5529 	 /* Acquire DB mutex */
5530 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5531 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5532 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5533 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5534 	 }
5535 #endif
5536 	 /* Point to the underlying storage engine */
5537 	 pEngine = unqlitePagerGetKvEngine(pDb);
5538 	 if( pEngine->pIo->pMethods->xAppend == 0 ){
5539 		 /* Storage engine does not implement such method */
5540 		 unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
5541 		 rc = UNQLITE_NOTIMPLEMENTED;
5542 	 }else{
5543 		 if( nKeyLen < 0 ){
5544 			 /* Assume a null terminated string and compute it's length */
5545 			 nKeyLen = SyStrlen((const char *)pKey);
5546 		 }
5547 		 if( !nKeyLen ){
5548 			 unqliteGenError(pDb,"Empty key");
5549 			 rc = UNQLITE_EMPTY;
5550 		 }else{
5551 			 SyBlob sWorker; /* Working buffer */
5552 			 va_list ap;
5553 			 SyBlobInit(&sWorker,&pDb->sMem);
5554 			 /* Format the data */
5555 			 va_start(ap,zFormat);
5556 			 SyBlobFormatAp(&sWorker,zFormat,ap);
5557 			 va_end(ap);
5558 			 /* Perform the requested operation */
5559 			 rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
5560 			 /* Clean up */
5561 			 SyBlobRelease(&sWorker);
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_fetch()]
5572  * Please refer to the official documentation for function purpose and expected parameters.
5573  */
unqlite_kv_fetch(unqlite * pDb,const void * pKey,int nKeyLen,void * pBuf,unqlite_int64 * pBufLen)5574 int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 *pBufLen)
5575 {
5576 	unqlite_kv_methods *pMethods;
5577 	unqlite_kv_engine *pEngine;
5578 	unqlite_kv_cursor *pCur;
5579 	int rc;
5580 	if( UNQLITE_DB_MISUSE(pDb) ){
5581 		return UNQLITE_CORRUPT;
5582 	}
5583 #if defined(UNQLITE_ENABLE_THREADS)
5584 	 /* Acquire DB mutex */
5585 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5586 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5587 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5588 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5589 	 }
5590 #endif
5591 	 /* Point to the underlying storage engine */
5592 	 pEngine = unqlitePagerGetKvEngine(pDb);
5593 	 pMethods = pEngine->pIo->pMethods;
5594 	 pCur = pDb->sDB.pCursor;
5595 	 if( nKeyLen < 0 ){
5596 		 /* Assume a null terminated string and compute it's length */
5597 		 nKeyLen = SyStrlen((const char *)pKey);
5598 	 }
5599 	 if( !nKeyLen ){
5600 		  unqliteGenError(pDb,"Empty key");
5601 		  rc = UNQLITE_EMPTY;
5602 	 }else{
5603 		  /* Seek to the record position */
5604 		  rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5605 	 }
5606 	 if( rc == UNQLITE_OK ){
5607 		 if( pBuf == 0 ){
5608 			 /* Data length only */
5609 			 rc = pMethods->xDataLength(pCur,pBufLen);
5610 		 }else{
5611 			 SyBlob sBlob;
5612 			 /* Initialize the data consumer */
5613 			 SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)*pBufLen);
5614 			 /* Consume the data */
5615 			 rc = pMethods->xData(pCur,unqliteDataConsumer,&sBlob);
5616 			 /* Data length */
5617 			 *pBufLen = (unqlite_int64)SyBlobLength(&sBlob);
5618 			 /* Cleanup */
5619 			 SyBlobRelease(&sBlob);
5620 		 }
5621 	 }
5622 #if defined(UNQLITE_ENABLE_THREADS)
5623 	 /* Leave DB mutex */
5624 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5625 #endif
5626 	return rc;
5627 }
5628 /*
5629  * [CAPIREF: unqlite_kv_fetch_callback()]
5630  * Please refer to the official documentation for function purpose and expected parameters.
5631  */
unqlite_kv_fetch_callback(unqlite * pDb,const void * pKey,int nKeyLen,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)5632 int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
5633 {
5634 	unqlite_kv_methods *pMethods;
5635 	unqlite_kv_engine *pEngine;
5636 	unqlite_kv_cursor *pCur;
5637 	int rc;
5638 	if( UNQLITE_DB_MISUSE(pDb) ){
5639 		return UNQLITE_CORRUPT;
5640 	}
5641 #if defined(UNQLITE_ENABLE_THREADS)
5642 	 /* Acquire DB mutex */
5643 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5644 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5645 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5646 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5647 	 }
5648 #endif
5649 	 /* Point to the underlying storage engine */
5650 	 pEngine = unqlitePagerGetKvEngine(pDb);
5651 	 pMethods = pEngine->pIo->pMethods;
5652 	 pCur = pDb->sDB.pCursor;
5653 	 if( nKeyLen < 0 ){
5654 		 /* Assume a null terminated string and compute it's length */
5655 		 nKeyLen = SyStrlen((const char *)pKey);
5656 	 }
5657 	 if( !nKeyLen ){
5658 		 unqliteGenError(pDb,"Empty key");
5659 		 rc = UNQLITE_EMPTY;
5660 	 }else{
5661 		 /* Seek to the record position */
5662 		 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5663 	 }
5664 	 if( rc == UNQLITE_OK && xConsumer ){
5665 		 /* Consume the data directly */
5666 		 rc = pMethods->xData(pCur,xConsumer,pUserData);
5667 	 }
5668 #if defined(UNQLITE_ENABLE_THREADS)
5669 	 /* Leave DB mutex */
5670 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5671 #endif
5672 	return rc;
5673 }
5674 /*
5675  * [CAPIREF: unqlite_kv_delete()]
5676  * Please refer to the official documentation for function purpose and expected parameters.
5677  */
unqlite_kv_delete(unqlite * pDb,const void * pKey,int nKeyLen)5678 int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen)
5679 {
5680 	unqlite_kv_methods *pMethods;
5681 	unqlite_kv_engine *pEngine;
5682 	unqlite_kv_cursor *pCur;
5683 	int rc;
5684 	if( UNQLITE_DB_MISUSE(pDb) ){
5685 		return UNQLITE_CORRUPT;
5686 	}
5687 #if defined(UNQLITE_ENABLE_THREADS)
5688 	 /* Acquire DB mutex */
5689 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5690 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5691 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5692 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5693 	 }
5694 #endif
5695 	 /* Point to the underlying storage engine */
5696 	 pEngine = unqlitePagerGetKvEngine(pDb);
5697 	 pMethods = pEngine->pIo->pMethods;
5698 	 pCur = pDb->sDB.pCursor;
5699 	 if( pMethods->xDelete == 0 ){
5700 		 /* Storage engine does not implement such method */
5701 		 unqliteGenError(pDb,"xDelete() method not implemented in the underlying storage engine");
5702 		 rc = UNQLITE_NOTIMPLEMENTED;
5703 	 }else{
5704 		 if( nKeyLen < 0 ){
5705 			 /* Assume a null terminated string and compute it's length */
5706 			 nKeyLen = SyStrlen((const char *)pKey);
5707 		 }
5708 		 if( !nKeyLen ){
5709 			 unqliteGenError(pDb,"Empty key");
5710 			 rc = UNQLITE_EMPTY;
5711 		 }else{
5712 			 /* Seek to the record position */
5713 			 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
5714 		 }
5715 		 if( rc == UNQLITE_OK ){
5716 			 /* Exact match found, delete the entry */
5717 			 rc = pMethods->xDelete(pCur);
5718 		 }
5719 	 }
5720 #if defined(UNQLITE_ENABLE_THREADS)
5721 	 /* Leave DB mutex */
5722 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5723 #endif
5724 	return rc;
5725 }
5726 /*
5727  * [CAPIREF: unqlite_kv_config()]
5728  * Please refer to the official documentation for function purpose and expected parameters.
5729  */
unqlite_kv_config(unqlite * pDb,int iOp,...)5730 int unqlite_kv_config(unqlite *pDb,int iOp,...)
5731 {
5732 	unqlite_kv_engine *pEngine;
5733 	int rc;
5734 	if( UNQLITE_DB_MISUSE(pDb) ){
5735 		return UNQLITE_CORRUPT;
5736 	}
5737 #if defined(UNQLITE_ENABLE_THREADS)
5738 	 /* Acquire DB mutex */
5739 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5740 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5741 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5742 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5743 	 }
5744 #endif
5745 	 /* Point to the underlying storage engine */
5746 	 pEngine = unqlitePagerGetKvEngine(pDb);
5747 	 if( pEngine->pIo->pMethods->xConfig == 0 ){
5748 		 /* Storage engine does not implements such method */
5749 		 unqliteGenError(pDb,"xConfig() method not implemented in the underlying storage engine");
5750 		 rc = UNQLITE_NOTIMPLEMENTED;
5751 	 }else{
5752 		 va_list ap;
5753 		 /* Configure the storage engine */
5754 		 va_start(ap,iOp);
5755 		 rc = pEngine->pIo->pMethods->xConfig(pEngine,iOp,ap);
5756 		 va_end(ap);
5757 	 }
5758 #if defined(UNQLITE_ENABLE_THREADS)
5759 	 /* Leave DB mutex */
5760 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5761 #endif
5762 	return rc;
5763 }
5764 /*
5765  * [CAPIREF: unqlite_kv_cursor_init()]
5766  * Please refer to the official documentation for function purpose and expected parameters.
5767  */
unqlite_kv_cursor_init(unqlite * pDb,unqlite_kv_cursor ** ppOut)5768 int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut)
5769 {
5770 	int rc;
5771 	if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0 /* Noop */){
5772 		return UNQLITE_CORRUPT;
5773 	}
5774 #if defined(UNQLITE_ENABLE_THREADS)
5775 	 /* Acquire DB mutex */
5776 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5777 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5778 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5779 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5780 	 }
5781 #endif
5782 	 /* Allocate a new cursor */
5783 	 rc = unqliteInitCursor(pDb,ppOut);
5784 #if defined(UNQLITE_ENABLE_THREADS)
5785 	 /* Leave DB mutex */
5786 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5787 #endif
5788 	 return rc;
5789 }
5790 /*
5791  * [CAPIREF: unqlite_kv_cursor_release()]
5792  * Please refer to the official documentation for function purpose and expected parameters.
5793  */
unqlite_kv_cursor_release(unqlite * pDb,unqlite_kv_cursor * pCur)5794 int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur)
5795 {
5796 	int rc;
5797 	if( UNQLITE_DB_MISUSE(pDb) || pCur == 0 /* Noop */){
5798 		return UNQLITE_CORRUPT;
5799 	}
5800 #if defined(UNQLITE_ENABLE_THREADS)
5801 	 /* Acquire DB mutex */
5802 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5803 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
5804 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
5805 			 return UNQLITE_ABORT; /* Another thread have released this instance */
5806 	 }
5807 #endif
5808 	 /* Release the cursor */
5809 	 rc = unqliteReleaseCursor(pDb,pCur);
5810 #if defined(UNQLITE_ENABLE_THREADS)
5811 	 /* Leave DB mutex */
5812 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
5813 #endif
5814 	 return rc;
5815 }
5816 /*
5817  * [CAPIREF: unqlite_kv_cursor_first_entry()]
5818  * Please refer to the official documentation for function purpose and expected parameters.
5819  */
unqlite_kv_cursor_first_entry(unqlite_kv_cursor * pCursor)5820 int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor)
5821 {
5822 	int rc;
5823 #ifdef UNTRUST
5824 	if( pCursor == 0 ){
5825 		return UNQLITE_CORRUPT;
5826 	}
5827 #endif
5828 	/* Check if the requested method is implemented by the underlying storage engine */
5829 	if( pCursor->pStore->pIo->pMethods->xFirst == 0 ){
5830 		rc = UNQLITE_NOTIMPLEMENTED;
5831 	}else{
5832 		/* Seek to the first entry */
5833 		rc = pCursor->pStore->pIo->pMethods->xFirst(pCursor);
5834 	}
5835 	return rc;
5836 }
5837 /*
5838  * [CAPIREF: unqlite_kv_cursor_last_entry()]
5839  * Please refer to the official documentation for function purpose and expected parameters.
5840  */
unqlite_kv_cursor_last_entry(unqlite_kv_cursor * pCursor)5841 int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor)
5842 {
5843 	int rc;
5844 #ifdef UNTRUST
5845 	if( pCursor == 0 ){
5846 		return UNQLITE_CORRUPT;
5847 	}
5848 #endif
5849 	/* Check if the requested method is implemented by the underlying storage engine */
5850 	if( pCursor->pStore->pIo->pMethods->xLast == 0 ){
5851 		rc = UNQLITE_NOTIMPLEMENTED;
5852 	}else{
5853 		/* Seek to the last entry */
5854 		rc = pCursor->pStore->pIo->pMethods->xLast(pCursor);
5855 	}
5856 	return rc;
5857 }
5858 /*
5859  * [CAPIREF: unqlite_kv_cursor_valid_entry()]
5860  * Please refer to the official documentation for function purpose and expected parameters.
5861  */
unqlite_kv_cursor_valid_entry(unqlite_kv_cursor * pCursor)5862 int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor)
5863 {
5864 	int rc;
5865 #ifdef UNTRUST
5866 	if( pCursor == 0 ){
5867 		return UNQLITE_CORRUPT;
5868 	}
5869 #endif
5870 	/* Check if the requested method is implemented by the underlying storage engine */
5871 	if( pCursor->pStore->pIo->pMethods->xValid == 0 ){
5872 		rc = UNQLITE_NOTIMPLEMENTED;
5873 	}else{
5874 		rc = pCursor->pStore->pIo->pMethods->xValid(pCursor);
5875 	}
5876 	return rc;
5877 }
5878 /*
5879  * [CAPIREF: unqlite_kv_cursor_next_entry()]
5880  * Please refer to the official documentation for function purpose and expected parameters.
5881  */
unqlite_kv_cursor_next_entry(unqlite_kv_cursor * pCursor)5882 int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor)
5883 {
5884 	int rc;
5885 #ifdef UNTRUST
5886 	if( pCursor == 0 ){
5887 		return UNQLITE_CORRUPT;
5888 	}
5889 #endif
5890 	/* Check if the requested method is implemented by the underlying storage engine */
5891 	if( pCursor->pStore->pIo->pMethods->xNext == 0 ){
5892 		rc = UNQLITE_NOTIMPLEMENTED;
5893 	}else{
5894 		/* Seek to the next entry */
5895 		rc = pCursor->pStore->pIo->pMethods->xNext(pCursor);
5896 	}
5897 	return rc;
5898 }
5899 /*
5900  * [CAPIREF: unqlite_kv_cursor_prev_entry()]
5901  * Please refer to the official documentation for function purpose and expected parameters.
5902  */
unqlite_kv_cursor_prev_entry(unqlite_kv_cursor * pCursor)5903 int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor)
5904 {
5905 	int rc;
5906 #ifdef UNTRUST
5907 	if( pCursor == 0 ){
5908 		return UNQLITE_CORRUPT;
5909 	}
5910 #endif
5911 	/* Check if the requested method is implemented by the underlying storage engine */
5912 	if( pCursor->pStore->pIo->pMethods->xPrev == 0 ){
5913 		rc = UNQLITE_NOTIMPLEMENTED;
5914 	}else{
5915 		/* Seek to the previous entry */
5916 		rc = pCursor->pStore->pIo->pMethods->xPrev(pCursor);
5917 	}
5918 	return rc;
5919 }
5920 /*
5921  * [CAPIREF: unqlite_kv_cursor_delete_entry()]
5922  * Please refer to the official documentation for function purpose and expected parameters.
5923  */
unqlite_kv_cursor_delete_entry(unqlite_kv_cursor * pCursor)5924 int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor)
5925 {
5926 	int rc;
5927 #ifdef UNTRUST
5928 	if( pCursor == 0 ){
5929 		return UNQLITE_CORRUPT;
5930 	}
5931 #endif
5932 	/* Check if the requested method is implemented by the underlying storage engine */
5933 	if( pCursor->pStore->pIo->pMethods->xDelete == 0 ){
5934 		rc = UNQLITE_NOTIMPLEMENTED;
5935 	}else{
5936 		/* Delete the entry */
5937 		rc = pCursor->pStore->pIo->pMethods->xDelete(pCursor);
5938 	}
5939 	return rc;
5940 }
5941 /*
5942  * [CAPIREF: unqlite_kv_cursor_reset()]
5943  * Please refer to the official documentation for function purpose and expected parameters.
5944  */
unqlite_kv_cursor_reset(unqlite_kv_cursor * pCursor)5945 int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor)
5946 {
5947 	int rc = UNQLITE_OK;
5948 #ifdef UNTRUST
5949 	if( pCursor == 0 ){
5950 		return UNQLITE_CORRUPT;
5951 	}
5952 #endif
5953 	/* Check if the requested method is implemented by the underlying storage engine */
5954 	if( pCursor->pStore->pIo->pMethods->xReset == 0 ){
5955 		rc = UNQLITE_NOTIMPLEMENTED;
5956 	}else{
5957 		/* Reset */
5958 		pCursor->pStore->pIo->pMethods->xReset(pCursor);
5959 	}
5960 	return rc;
5961 }
5962 /*
5963  * [CAPIREF: unqlite_kv_cursor_seek()]
5964  * Please refer to the official documentation for function purpose and expected parameters.
5965  */
unqlite_kv_cursor_seek(unqlite_kv_cursor * pCursor,const void * pKey,int nKeyLen,int iPos)5966 int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos)
5967 {
5968 	int rc = UNQLITE_OK;
5969 #ifdef UNTRUST
5970 	if( pCursor == 0 ){
5971 		return UNQLITE_CORRUPT;
5972 	}
5973 #endif
5974 	if( nKeyLen < 0 ){
5975 		/* Assume a null terminated string and compute it's length */
5976 		nKeyLen = SyStrlen((const char *)pKey);
5977 	}
5978 	if( !nKeyLen ){
5979 		rc = UNQLITE_EMPTY;
5980 	}else{
5981 		/* Seek to the desired location */
5982 		rc = pCursor->pStore->pIo->pMethods->xSeek(pCursor,pKey,nKeyLen,iPos);
5983 	}
5984 	return rc;
5985 }
5986 /*
5987  * Default data consumer callback. That is, all retrieved is redirected to this
5988  * routine which store the output in an internal blob.
5989  */
unqliteDataConsumer(const void * pOut,unsigned int nLen,void * pUserData)5990 UNQLITE_PRIVATE int unqliteDataConsumer(
5991 	const void *pOut,   /* Data to consume */
5992 	unsigned int nLen,  /* Data length */
5993 	void *pUserData     /* User private data */
5994 	)
5995 {
5996 	 sxi32 rc;
5997 	 /* Store the output in an internal BLOB */
5998 	 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
5999 	 return rc;
6000 }
6001 /*
6002  * [CAPIREF: unqlite_kv_cursor_data_callback()]
6003  * Please refer to the official documentation for function purpose and expected parameters.
6004  */
unqlite_kv_cursor_key_callback(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)6005 int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
6006 {
6007 	int rc;
6008 #ifdef UNTRUST
6009 	if( pCursor == 0 ){
6010 		return UNQLITE_CORRUPT;
6011 	}
6012 #endif
6013 	/* Consume the key directly */
6014 	rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,xConsumer,pUserData);
6015 	return rc;
6016 }
6017 /*
6018  * [CAPIREF: unqlite_kv_cursor_key()]
6019  * Please refer to the official documentation for function purpose and expected parameters.
6020  */
unqlite_kv_cursor_key(unqlite_kv_cursor * pCursor,void * pBuf,int * pnByte)6021 int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte)
6022 {
6023 	int rc;
6024 #ifdef UNTRUST
6025 	if( pCursor == 0 ){
6026 		return UNQLITE_CORRUPT;
6027 	}
6028 #endif
6029 	if( pBuf == 0 ){
6030 		/* Key length only */
6031 		rc = pCursor->pStore->pIo->pMethods->xKeyLength(pCursor,pnByte);
6032 	}else{
6033 		SyBlob sBlob;
6034 		if( (*pnByte) < 0 ){
6035 			return UNQLITE_CORRUPT;
6036 		}
6037 		/* Initialize the data consumer */
6038 		SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
6039 		/* Consume the key */
6040 		rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,unqliteDataConsumer,&sBlob);
6041 		 /* Key length */
6042 		*pnByte = SyBlobLength(&sBlob);
6043 		/* Cleanup */
6044 		SyBlobRelease(&sBlob);
6045 	}
6046 	return rc;
6047 }
6048 /*
6049  * [CAPIREF: unqlite_kv_cursor_data_callback()]
6050  * Please refer to the official documentation for function purpose and expected parameters.
6051  */
unqlite_kv_cursor_data_callback(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)6052 int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
6053 {
6054 	int rc;
6055 #ifdef UNTRUST
6056 	if( pCursor == 0 ){
6057 		return UNQLITE_CORRUPT;
6058 	}
6059 #endif
6060 	/* Consume the data directly */
6061 	rc = pCursor->pStore->pIo->pMethods->xData(pCursor,xConsumer,pUserData);
6062 	return rc;
6063 }
6064 /*
6065  * [CAPIREF: unqlite_kv_cursor_data()]
6066  * Please refer to the official documentation for function purpose and expected parameters.
6067  */
unqlite_kv_cursor_data(unqlite_kv_cursor * pCursor,void * pBuf,unqlite_int64 * pnByte)6068 int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnByte)
6069 {
6070 	int rc;
6071 #ifdef UNTRUST
6072 	if( pCursor == 0 ){
6073 		return UNQLITE_CORRUPT;
6074 	}
6075 #endif
6076 	if( pBuf == 0 ){
6077 		/* Data length only */
6078 		rc = pCursor->pStore->pIo->pMethods->xDataLength(pCursor,pnByte);
6079 	}else{
6080 		SyBlob sBlob;
6081 		if( (*pnByte) < 0 ){
6082 			return UNQLITE_CORRUPT;
6083 		}
6084 		/* Initialize the data consumer */
6085 		SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
6086 		/* Consume the data */
6087 		rc = pCursor->pStore->pIo->pMethods->xData(pCursor,unqliteDataConsumer,&sBlob);
6088 		/* Data length */
6089 		*pnByte = SyBlobLength(&sBlob);
6090 		/* Cleanup */
6091 		SyBlobRelease(&sBlob);
6092 	}
6093 	return rc;
6094 }
6095 /*
6096  * [CAPIREF: unqlite_begin()]
6097  * Please refer to the official documentation for function purpose and expected parameters.
6098  */
unqlite_begin(unqlite * pDb)6099 int unqlite_begin(unqlite *pDb)
6100 {
6101 	int rc;
6102 	if( UNQLITE_DB_MISUSE(pDb) ){
6103 		return UNQLITE_CORRUPT;
6104 	}
6105 #if defined(UNQLITE_ENABLE_THREADS)
6106 	 /* Acquire DB mutex */
6107 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6108 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6109 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
6110 			 return UNQLITE_ABORT; /* Another thread have released this instance */
6111 	 }
6112 #endif
6113 	 /* Begin the write transaction */
6114 	 rc = unqlitePagerBegin(pDb->sDB.pPager);
6115 #if defined(UNQLITE_ENABLE_THREADS)
6116 	 /* Leave DB mutex */
6117 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6118 #endif
6119 	 return rc;
6120 }
6121 /*
6122  * [CAPIREF: unqlite_commit()]
6123  * Please refer to the official documentation for function purpose and expected parameters.
6124  */
unqlite_commit(unqlite * pDb)6125 int unqlite_commit(unqlite *pDb)
6126 {
6127 	int rc;
6128 	if( UNQLITE_DB_MISUSE(pDb) ){
6129 		return UNQLITE_CORRUPT;
6130 	}
6131 #if defined(UNQLITE_ENABLE_THREADS)
6132 	 /* Acquire DB mutex */
6133 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6134 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6135 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
6136 			 return UNQLITE_ABORT; /* Another thread have released this instance */
6137 	 }
6138 #endif
6139 	 /* Commit the transaction */
6140 	 rc = unqlitePagerCommit(pDb->sDB.pPager);
6141 #if defined(UNQLITE_ENABLE_THREADS)
6142 	 /* Leave DB mutex */
6143 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6144 #endif
6145 	 return rc;
6146 }
6147 /*
6148  * [CAPIREF: unqlite_rollback()]
6149  * Please refer to the official documentation for function purpose and expected parameters.
6150  */
unqlite_rollback(unqlite * pDb)6151 int unqlite_rollback(unqlite *pDb)
6152 {
6153 	int rc;
6154 	if( UNQLITE_DB_MISUSE(pDb) ){
6155 		return UNQLITE_CORRUPT;
6156 	}
6157 #if defined(UNQLITE_ENABLE_THREADS)
6158 	 /* Acquire DB mutex */
6159 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6160 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6161 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
6162 			 return UNQLITE_ABORT; /* Another thread have released this instance */
6163 	 }
6164 #endif
6165 	 /* Rollback the transaction */
6166 	 rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
6167 #if defined(UNQLITE_ENABLE_THREADS)
6168 	 /* Leave DB mutex */
6169 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6170 #endif
6171 	 return rc;
6172 }
6173 /*
6174  * [CAPIREF: unqlite_util_load_mmaped_file()]
6175  * Please refer to the official documentation for function purpose and expected parameters.
6176  */
unqlite_util_load_mmaped_file(const char * zFile,void ** ppMap,unqlite_int64 * pFileSize)6177 UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize)
6178 {
6179 	const jx9_vfs *pVfs;
6180 	int rc;
6181 	if( SX_EMPTY_STR(zFile) || ppMap == 0 || pFileSize == 0){
6182 		/* Sanity check */
6183 		return UNQLITE_CORRUPT;
6184 	}
6185 	*ppMap = 0;
6186 	/* Extract the Jx9 Vfs */
6187 	pVfs = jx9ExportBuiltinVfs();
6188 	/*
6189 	 * Check if the underlying vfs implement the memory map routines
6190 	 * [i.e: mmap() under UNIX/MapViewOfFile() under windows].
6191 	 */
6192 	if( pVfs == 0 || pVfs->xMmap == 0 ){
6193 		rc = UNQLITE_NOTIMPLEMENTED;
6194 	 }else{
6195 		 /* Try to get a read-only memory view of the whole file */
6196 		 rc = pVfs->xMmap(zFile,ppMap,pFileSize);
6197 	 }
6198 	return rc;
6199 }
6200 /*
6201  * [CAPIREF: unqlite_util_release_mmaped_file()]
6202  * Please refer to the official documentation for function purpose and expected parameters.
6203  */
unqlite_util_release_mmaped_file(void * pMap,unqlite_int64 iFileSize)6204 UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize)
6205 {
6206 	const jx9_vfs *pVfs;
6207 	int rc = UNQLITE_OK;
6208 	if( pMap == 0 ){
6209 		return UNQLITE_OK;
6210 	}
6211 	/* Extract the Jx9 Vfs */
6212 	pVfs = jx9ExportBuiltinVfs();
6213 	if( pVfs == 0 || pVfs->xUnmap == 0 ){
6214 		rc = UNQLITE_NOTIMPLEMENTED;
6215 	 }else{
6216 		 pVfs->xUnmap(pMap,iFileSize);
6217 	 }
6218 	return rc;
6219 }
6220 /*
6221  * [CAPIREF: unqlite_util_random_string()]
6222  * Please refer to the official documentation for function purpose and expected parameters.
6223  */
unqlite_util_random_string(unqlite * pDb,char * zBuf,unsigned int buf_size)6224 UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size)
6225 {
6226 	if( UNQLITE_DB_MISUSE(pDb) ){
6227 		return UNQLITE_CORRUPT;
6228 	}
6229 	if( zBuf == 0 || buf_size < 3 ){
6230 		/* Buffer must be long enough to hold three bytes */
6231 		return UNQLITE_INVALID;
6232 	}
6233 #if defined(UNQLITE_ENABLE_THREADS)
6234 	 /* Acquire DB mutex */
6235 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6236 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6237 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
6238 			 return UNQLITE_ABORT; /* Another thread have released this instance */
6239 	 }
6240 #endif
6241 	 /* Generate the random string */
6242 	 unqlitePagerRandomString(pDb->sDB.pPager,zBuf,buf_size);
6243 #if defined(UNQLITE_ENABLE_THREADS)
6244 	 /* Leave DB mutex */
6245 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6246 #endif
6247 	 return UNQLITE_OK;
6248 }
6249 /*
6250  * [CAPIREF: unqlite_util_random_num()]
6251  * Please refer to the official documentation for function purpose and expected parameters.
6252  */
unqlite_util_random_num(unqlite * pDb)6253 UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb)
6254 {
6255 	sxu32 iNum;
6256 	if( UNQLITE_DB_MISUSE(pDb) ){
6257 		return 0;
6258 	}
6259 #if defined(UNQLITE_ENABLE_THREADS)
6260 	 /* Acquire DB mutex */
6261 	 SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6262 	 if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE &&
6263 		 UNQLITE_THRD_DB_RELEASE(pDb) ){
6264 			 return 0; /* Another thread have released this instance */
6265 	 }
6266 #endif
6267 	 /* Generate the random number */
6268 	 iNum = unqlitePagerRandomNum(pDb->sDB.pPager);
6269 #if defined(UNQLITE_ENABLE_THREADS)
6270 	 /* Leave DB mutex */
6271 	 SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
6272 #endif
6273 	 return iNum;
6274 }
6275 
6276 /* bitvec.c */
6277 /*
6278  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
6279  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
6280  * Version 1.1.6
6281  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6282  * please contact Symisc Systems via:
6283  *       legal@symisc.net
6284  *       licensing@symisc.net
6285  *       contact@symisc.net
6286  * or visit:
6287  *      http://unqlite.org/licensing.html
6288  */
6289  /* $SymiscID: bitvec.c v1.0 Win7 2013-02-27 15:16 stable <chm@symisc.net> $ */
6290 #ifndef UNQLITE_AMALGAMATION
6291 #include "unqliteInt.h"
6292 #endif
6293 
6294 /** This file implements an object that represents a dynmaic
6295 ** bitmap.
6296 **
6297 ** A bitmap is used to record which pages of a database file have been
6298 ** journalled during a transaction, or which pages have the "dont-write"
6299 ** property.  Usually only a few pages are meet either condition.
6300 ** So the bitmap is usually sparse and has low cardinality.
6301 */
6302 /*
6303  * Actually, this is not a bitmap but a simple hashtable where page
6304  * number (64-bit unsigned integers) are used as the lookup keys.
6305  */
6306 typedef struct bitvec_rec bitvec_rec;
6307 struct bitvec_rec
6308 {
6309 	pgno iPage;                  /* Page number */
6310 	bitvec_rec *pNext,*pNextCol; /* Collison link */
6311 };
6312 struct Bitvec
6313 {
6314 	SyMemBackend *pAlloc; /* Memory allocator */
6315 	sxu32 nRec;           /* Total number of records */
6316 	sxu32 nSize;          /* Table size */
6317 	bitvec_rec **apRec;   /* Record table */
6318 	bitvec_rec *pList;    /* List of records */
6319 };
6320 /*
6321  * Allocate a new bitvec instance.
6322 */
unqliteBitvecCreate(SyMemBackend * pAlloc,pgno iSize)6323 UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize)
6324 {
6325 	bitvec_rec **apNew;
6326 	Bitvec *p;
6327 
6328 	p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) );
6329 	if( p == 0 ){
6330 		SXUNUSED(iSize); /* cc warning */
6331 		return 0;
6332 	}
6333 	/* Zero the structure */
6334 	SyZero(p,sizeof(Bitvec));
6335 	/* Allocate a new table */
6336 	p->nSize = 64; /* Must be a power of two */
6337 	apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *));
6338 	if( apNew == 0 ){
6339 		SyMemBackendFree(pAlloc,p);
6340 		return 0;
6341 	}
6342 	/* Zero the new table */
6343 	SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *));
6344 	/* Fill-in */
6345 	p->apRec = apNew;
6346 	p->pAlloc = pAlloc;
6347 	return p;
6348 }
6349 /*
6350  * Check if the given page number is already installed in the table.
6351  * Return true if installed. False otherwise.
6352  */
unqliteBitvecTest(Bitvec * p,pgno i)6353 UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i)
6354 {
6355 	bitvec_rec *pRec;
6356 	/* Point to the desired bucket */
6357 	pRec = p->apRec[i & (p->nSize - 1)];
6358 	for(;;){
6359 		if( pRec == 0 ){ break; }
6360 		if( pRec->iPage == i ){
6361 			/* Page found */
6362 			return 1;
6363 		}
6364 		/* Point to the next entry */
6365 		pRec = pRec->pNextCol;
6366 
6367 		if( pRec == 0 ){ break; }
6368 		if( pRec->iPage == i ){
6369 			/* Page found */
6370 			return 1;
6371 		}
6372 		/* Point to the next entry */
6373 		pRec = pRec->pNextCol;
6374 
6375 
6376 		if( pRec == 0 ){ break; }
6377 		if( pRec->iPage == i ){
6378 			/* Page found */
6379 			return 1;
6380 		}
6381 		/* Point to the next entry */
6382 		pRec = pRec->pNextCol;
6383 
6384 
6385 		if( pRec == 0 ){ break; }
6386 		if( pRec->iPage == i ){
6387 			/* Page found */
6388 			return 1;
6389 		}
6390 		/* Point to the next entry */
6391 		pRec = pRec->pNextCol;
6392 	}
6393 	/* No such entry */
6394 	return 0;
6395 }
6396 /*
6397  * Install a given page number in our bitmap (Actually, our hashtable).
6398  */
unqliteBitvecSet(Bitvec * p,pgno i)6399 UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i)
6400 {
6401 	bitvec_rec *pRec;
6402 	sxi32 iBuck;
6403 	/* Allocate a new instance */
6404 	pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec));
6405 	if( pRec == 0 ){
6406 		return UNQLITE_NOMEM;
6407 	}
6408 	/* Zero the structure */
6409 	SyZero(pRec,sizeof(bitvec_rec));
6410 	/* Fill-in */
6411 	pRec->iPage = i;
6412 	iBuck = i & (p->nSize - 1);
6413 	pRec->pNextCol = p->apRec[iBuck];
6414 	p->apRec[iBuck] = pRec;
6415 	pRec->pNext = p->pList;
6416 	p->pList = pRec;
6417 	p->nRec++;
6418 	if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){
6419 		/* Grow the hashtable */
6420 		sxu32 nNewSize = p->nSize << 1;
6421 		bitvec_rec *pEntry,**apNew;
6422 		sxu32 n;
6423 		apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *));
6424 		if( apNew ){
6425 			sxu32 iBucket;
6426 			/* Zero the new table */
6427 			SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *));
6428 			/* Rehash all entries */
6429 			n = 0;
6430 			pEntry = p->pList;
6431 			for(;;){
6432 				/* Loop one */
6433 				if( n >= p->nRec ){
6434 					break;
6435 				}
6436 				pEntry->pNextCol = 0;
6437 				/* Install in the new bucket */
6438 				iBucket = pEntry->iPage & (nNewSize - 1);
6439 				pEntry->pNextCol = apNew[iBucket];
6440 				apNew[iBucket] = pEntry;
6441 				/* Point to the next entry */
6442 				pEntry = pEntry->pNext;
6443 				n++;
6444 			}
6445 			/* Release the old table and reflect the change */
6446 			SyMemBackendFree(p->pAlloc,(void *)p->apRec);
6447 			p->apRec = apNew;
6448 			p->nSize  = nNewSize;
6449 		}
6450 	}
6451 	return UNQLITE_OK;
6452 }
6453 /*
6454  * Destroy a bitvec instance. Reclaim all memory used.
6455  */
unqliteBitvecDestroy(Bitvec * p)6456 UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p)
6457 {
6458 	bitvec_rec *pNext,*pRec = p->pList;
6459 	SyMemBackend *pAlloc = p->pAlloc;
6460 
6461 	for(;;){
6462 		if( p->nRec < 1 ){
6463 			break;
6464 		}
6465 		pNext = pRec->pNext;
6466 		SyMemBackendPoolFree(pAlloc,(void *)pRec);
6467 		pRec = pNext;
6468 		p->nRec--;
6469 
6470 		if( p->nRec < 1 ){
6471 			break;
6472 		}
6473 		pNext = pRec->pNext;
6474 		SyMemBackendPoolFree(pAlloc,(void *)pRec);
6475 		pRec = pNext;
6476 		p->nRec--;
6477 
6478 
6479 		if( p->nRec < 1 ){
6480 			break;
6481 		}
6482 		pNext = pRec->pNext;
6483 		SyMemBackendPoolFree(pAlloc,(void *)pRec);
6484 		pRec = pNext;
6485 		p->nRec--;
6486 
6487 
6488 		if( p->nRec < 1 ){
6489 			break;
6490 		}
6491 		pNext = pRec->pNext;
6492 		SyMemBackendPoolFree(pAlloc,(void *)pRec);
6493 		pRec = pNext;
6494 		p->nRec--;
6495 	}
6496 	SyMemBackendFree(pAlloc,(void *)p->apRec);
6497 	SyMemBackendFree(pAlloc,p);
6498 }
6499 
6500 /* fastjson.c */
6501 /*
6502  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
6503  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
6504  * Version 1.1.6
6505  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6506  * please contact Symisc Systems via:
6507  *       legal@symisc.net
6508  *       licensing@symisc.net
6509  *       contact@symisc.net
6510  * or visit:
6511  *      http://unqlite.org/licensing.html
6512  */
6513  /* $SymiscID: fastjson.c v1.1 FreeBSD 2012-12-05 22:52 stable <chm@symisc.net> $ */
6514 #ifndef UNQLITE_AMALGAMATION
6515 #include "unqliteInt.h"
6516 #endif
6517 /* JSON binary encoding, decoding and stuff like that */
6518 #ifndef UNQLITE_FAST_JSON_NEST_LIMIT
6519 #if defined(__WINNT__) || defined(__UNIXES__)
6520 #define UNQLITE_FAST_JSON_NEST_LIMIT 64 /* Nesting limit */
6521 #else
6522 #define UNQLITE_FAST_JSON_NEST_LIMIT 32 /* Nesting limit */
6523 #endif
6524 #endif /* UNQLITE_FAST_JSON_NEST_LIMIT */
6525 /*
6526  * JSON to Binary using the FastJSON implementation (BigEndian).
6527  */
6528 /*
6529  * FastJSON implemented binary token.
6530  */
6531 #define FJSON_DOC_START    1 /* { */
6532 #define FJSON_DOC_END      2 /* } */
6533 #define FJSON_ARRAY_START  3 /* [ */
6534 #define FJSON_ARRAY_END    4 /* ] */
6535 #define FJSON_COLON        5 /* : */
6536 #define FJSON_COMMA        6 /* , */
6537 #define FJSON_ID           7 /* ID + 4 Bytes length */
6538 #define FJSON_STRING       8 /* String + 4 bytes length */
6539 #define FJSON_BYTE         9 /* Byte */
6540 #define FJSON_INT64       10 /* Integer 64 + 8 bytes */
6541 #define FJSON_REAL        18 /* Floating point value + 2 bytes */
6542 #define FJSON_NULL        23 /* NULL */
6543 #define FJSON_TRUE        24 /* TRUE */
6544 #define FJSON_FALSE       25 /* FALSE */
6545 /*
6546  * Encode a Jx9 value to binary JSON.
6547  */
FastJsonEncode(jx9_value * pValue,SyBlob * pOut,int iNest)6548 UNQLITE_PRIVATE sxi32 FastJsonEncode(
6549 	jx9_value *pValue, /* Value to encode */
6550 	SyBlob *pOut,      /* Store encoded value here */
6551 	int iNest          /* Nesting limit */
6552 	)
6553 {
6554 	sxi32 iType = pValue ? pValue->iFlags : MEMOBJ_NULL;
6555 	sxi32 rc = SXRET_OK;
6556 	int c;
6557 	if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
6558 		/* Nesting limit reached */
6559 		return SXERR_LIMIT;
6560 	}
6561 	if( iType & (MEMOBJ_NULL|MEMOBJ_RES) ){
6562 		/*
6563 		 * Resources are encoded as null also.
6564 		 */
6565 		c = FJSON_NULL;
6566 		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6567 	}else if( iType & MEMOBJ_BOOL ){
6568 		c = pValue->x.iVal ? FJSON_TRUE : FJSON_FALSE;
6569 		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6570 	}else if( iType & MEMOBJ_STRING ){
6571 		unsigned char zBuf[sizeof(sxu32)]; /* String length */
6572 		c = FJSON_STRING;
6573 		SyBigEndianPack32(zBuf,SyBlobLength(&pValue->sBlob));
6574 		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6575 		if( rc == SXRET_OK ){
6576 			rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
6577 			if( rc == SXRET_OK ){
6578 				rc = SyBlobAppend(pOut,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob));
6579 			}
6580 		}
6581 	}else if( iType & MEMOBJ_INT ){
6582 		unsigned char zBuf[8];
6583 		/* 64bit big endian integer */
6584 		c = FJSON_INT64;
6585 		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6586 		if( rc == SXRET_OK ){
6587 			SyBigEndianPack64(zBuf,(sxu64)pValue->x.iVal);
6588 			rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
6589 		}
6590 	}else if( iType & MEMOBJ_REAL ){
6591 		/* Real number */
6592 		c = FJSON_REAL;
6593 		rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6594 		if( rc == SXRET_OK ){
6595 			sxu32 iOfft = SyBlobLength(pOut);
6596 			rc = SyBlobAppendBig16(pOut,0);
6597 			if( rc == SXRET_OK ){
6598 				unsigned char *zBlob;
6599 				SyBlobFormat(pOut,"%.15g",pValue->x.rVal);
6600 				zBlob = (unsigned char *)SyBlobDataAt(pOut,iOfft);
6601 				SyBigEndianPack16(zBlob,(sxu16)(SyBlobLength(pOut) - ( 2 + iOfft)));
6602 			}
6603 		}
6604 	}else if( iType & MEMOBJ_HASHMAP ){
6605 		/* A JSON object or array */
6606 		jx9_hashmap *pMap = (jx9_hashmap *)pValue->x.pOther;
6607 		jx9_hashmap_node *pNode;
6608 		jx9_value *pEntry;
6609 		/* Reset the hashmap loop cursor */
6610 		jx9HashmapResetLoopCursor(pMap);
6611 		if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
6612 			jx9_value sKey;
6613 			/* A JSON object */
6614 			c = FJSON_DOC_START; /* { */
6615 			rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6616 			if( rc == SXRET_OK ){
6617 				jx9MemObjInit(pMap->pVm,&sKey);
6618 				/* Encode object entries */
6619 				while((pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
6620 					/* Extract the key */
6621 					jx9HashmapExtractNodeKey(pNode,&sKey);
6622 					/* Encode it */
6623 					rc = FastJsonEncode(&sKey,pOut,iNest+1);
6624 					if( rc != SXRET_OK ){
6625 						break;
6626 					}
6627 					c = FJSON_COLON;
6628 					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6629 					if( rc != SXRET_OK ){
6630 						break;
6631 					}
6632 					/* Extract the value */
6633 					pEntry = jx9HashmapGetNodeValue(pNode);
6634 					/* Encode it */
6635 					rc = FastJsonEncode(pEntry,pOut,iNest+1);
6636 					if( rc != SXRET_OK ){
6637 						break;
6638 					}
6639 					/* Delimit the entry */
6640 					c = FJSON_COMMA;
6641 					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6642 					if( rc != SXRET_OK ){
6643 						break;
6644 					}
6645 				}
6646 				jx9MemObjRelease(&sKey);
6647 				if( rc == SXRET_OK ){
6648 					c = FJSON_DOC_END; /* } */
6649 					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6650 				}
6651 			}
6652 		}else{
6653 			/* A JSON array */
6654 			c = FJSON_ARRAY_START; /* [ */
6655 			rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6656 			if( rc == SXRET_OK ){
6657 				/* Encode array entries */
6658 				while( (pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
6659 					/* Extract the value */
6660 					pEntry = jx9HashmapGetNodeValue(pNode);
6661 					/* Encode it */
6662 					rc = FastJsonEncode(pEntry,pOut,iNest+1);
6663 					if( rc != SXRET_OK ){
6664 						break;
6665 					}
6666 					/* Delimit the entry */
6667 					c = FJSON_COMMA;
6668 					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6669 					if( rc != SXRET_OK ){
6670 						break;
6671 					}
6672 				}
6673 				if( rc == SXRET_OK ){
6674 					c = FJSON_ARRAY_END; /* ] */
6675 					rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
6676 				}
6677 			}
6678 		}
6679 	}
6680 	return rc;
6681 }
6682 /*
6683  * Decode a FastJSON binary blob.
6684  */
FastJsonDecode(const void * pIn,sxu32 nByte,jx9_value * pOut,const unsigned char ** pzPtr,int iNest)6685 UNQLITE_PRIVATE sxi32 FastJsonDecode(
6686 	const void *pIn,  /* Binary JSON  */
6687 	sxu32 nByte,      /* Chunk delimiter */
6688 	jx9_value *pOut,  /* Decoded value */
6689 	const unsigned char **pzPtr,
6690 	int iNest /* Nesting limit */
6691 	)
6692 {
6693 	const unsigned char *zIn = (const unsigned char *)pIn;
6694 	const unsigned char *zEnd = &zIn[nByte];
6695 	sxi32 rc = SXRET_OK;
6696 	int c;
6697 	if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
6698 		/* Nesting limit reached */
6699 		return SXERR_LIMIT;
6700 	}
6701 	c = zIn[0];
6702 	/* Advance the stream cursor */
6703 	zIn++;
6704 	/* Process the binary token */
6705 	switch(c){
6706 	case FJSON_NULL:
6707 		/* null */
6708 		jx9_value_null(pOut);
6709 		break;
6710 	case FJSON_FALSE:
6711 		/* Boolean FALSE */
6712 		jx9_value_bool(pOut,0);
6713 		break;
6714 	case FJSON_TRUE:
6715 		/* Boolean TRUE */
6716 		jx9_value_bool(pOut,1);
6717 		break;
6718 	case FJSON_INT64: {
6719 		/* 64Bit integer */
6720 		sxu64 iVal;
6721 		/* Sanity check */
6722 		if( &zIn[8] >= zEnd ){
6723 			/* Corrupt chunk */
6724 			rc = SXERR_CORRUPT;
6725 			break;
6726 		}
6727 		SyBigEndianUnpack64(zIn,&iVal);
6728 		/* Advance the pointer */
6729 		zIn += 8;
6730 		jx9_value_int64(pOut,(jx9_int64)iVal);
6731 		break;
6732 					  }
6733 	case FJSON_REAL: {
6734 		/* Real number */
6735 		double iVal = 0; /* cc warning */
6736 		sxu16 iLen;
6737 		/* Sanity check */
6738 		if( &zIn[2] >= zEnd ){
6739 			/* Corrupt chunk */
6740 			rc = SXERR_CORRUPT;
6741 			break;
6742 		}
6743 		SyBigEndianUnpack16(zIn,&iLen);
6744 		if( &zIn[iLen] >= zEnd ){
6745 			/* Corrupt chunk */
6746 			rc = SXERR_CORRUPT;
6747 			break;
6748 		}
6749 		zIn += 2;
6750 		SyStrToReal((const char *)zIn,(sxu32)iLen,&iVal,0);
6751 		/* Advance the pointer */
6752 		zIn += iLen;
6753 		jx9_value_double(pOut,iVal);
6754 		break;
6755 					 }
6756 	case FJSON_STRING: {
6757 		/* UTF-8/Binary chunk */
6758 		sxu32 iLength;
6759 		/* Sanity check */
6760 		if( &zIn[4] >= zEnd ){
6761 			/* Corrupt chunk */
6762 			rc = SXERR_CORRUPT;
6763 			break;
6764 		}
6765 		SyBigEndianUnpack32(zIn,&iLength);
6766 		if( &zIn[iLength] >= zEnd ){
6767 			/* Corrupt chunk */
6768 			rc = SXERR_CORRUPT;
6769 			break;
6770 		}
6771 		zIn += 4;
6772 		/* Invalidate any prior representation */
6773 		if( pOut->iFlags & MEMOBJ_STRING ){
6774 			/* Reset the string cursor */
6775 			SyBlobReset(&pOut->sBlob);
6776 		}
6777 		rc = jx9MemObjStringAppend(pOut,(const char *)zIn,iLength);
6778 		/* Update pointer */
6779 		zIn += iLength;
6780 		break;
6781 					   }
6782 	case FJSON_ARRAY_START: {
6783 		/* Binary JSON array */
6784 		jx9_hashmap *pMap;
6785 		jx9_value sVal;
6786 		/* Allocate a new hashmap */
6787 		pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
6788 		if( pMap == 0 ){
6789 			rc = SXERR_MEM;
6790 			break;
6791 		}
6792 		jx9MemObjInit(pOut->pVm,&sVal);
6793 		jx9MemObjRelease(pOut);
6794 		MemObjSetType(pOut,MEMOBJ_HASHMAP);
6795 		pOut->x.pOther = pMap;
6796 		rc = SXRET_OK;
6797 		for(;;){
6798 			/* Jump leading binary commas */
6799 			while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
6800 				zIn++;
6801 			}
6802 			if( zIn >= zEnd || zIn[0] == FJSON_ARRAY_END ){
6803 				if( zIn < zEnd ){
6804 					zIn++; /* Jump the trailing binary ] */
6805 				}
6806 				break;
6807 			}
6808 			/* Decode the value */
6809 			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
6810 			if( rc != SXRET_OK ){
6811 				break;
6812 			}
6813 			/* Insert the decoded value */
6814 			rc = jx9HashmapInsert(pMap,0,&sVal);
6815 			if( rc != UNQLITE_OK ){
6816 				break;
6817 			}
6818 		}
6819 		if( rc != SXRET_OK ){
6820 			jx9MemObjRelease(pOut);
6821 		}
6822 		jx9MemObjRelease(&sVal);
6823 		break;
6824 							}
6825 	case FJSON_DOC_START: {
6826 		/* Binary JSON object */
6827 		jx9_value sVal,sKey;
6828 		jx9_hashmap *pMap;
6829 		/* Allocate a new hashmap */
6830 		pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
6831 		if( pMap == 0 ){
6832 			rc = SXERR_MEM;
6833 			break;
6834 		}
6835 		jx9MemObjInit(pOut->pVm,&sVal);
6836 		jx9MemObjInit(pOut->pVm,&sKey);
6837 		jx9MemObjRelease(pOut);
6838 		MemObjSetType(pOut,MEMOBJ_HASHMAP);
6839 		pOut->x.pOther = pMap;
6840 		rc = SXRET_OK;
6841 		for(;;){
6842 			/* Jump leading binary commas */
6843 			while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
6844 				zIn++;
6845 			}
6846 			if( zIn >= zEnd || zIn[0] == FJSON_DOC_END ){
6847 				if( zIn < zEnd ){
6848 					zIn++; /* Jump the trailing binary } */
6849 				}
6850 				break;
6851 			}
6852 			/* Extract the key */
6853 			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sKey,&zIn,iNest+1);
6854 			if( rc != UNQLITE_OK ){
6855 				break;
6856 			}
6857 			if( zIn >= zEnd || zIn[0] != FJSON_COLON ){
6858 				rc = UNQLITE_CORRUPT;
6859 				break;
6860 			}
6861 			zIn++; /* Jump the binary colon ':' */
6862 			if( zIn >= zEnd ){
6863 				rc = UNQLITE_CORRUPT;
6864 				break;
6865 			}
6866 			/* Decode the value */
6867 			rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
6868 			if( rc != SXRET_OK ){
6869 				break;
6870 			}
6871 			/* Insert the key and its associated value */
6872 			rc = jx9HashmapInsert(pMap,&sKey,&sVal);
6873 			if( rc != UNQLITE_OK ){
6874 				break;
6875 			}
6876 		}
6877 		if( rc != SXRET_OK ){
6878 			jx9MemObjRelease(pOut);
6879 		}
6880 		jx9MemObjRelease(&sVal);
6881 		jx9MemObjRelease(&sKey);
6882 		break;
6883 						  }
6884 	default:
6885 		/* Corrupt data */
6886 		rc = SXERR_CORRUPT;
6887 		break;
6888 	}
6889 	if( pzPtr ){
6890 		*pzPtr = zIn;
6891 	}
6892 	return rc;
6893 }
6894 
6895 /* jx9_api.c */
6896 /*
6897  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
6898  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
6899  * Version 1.7.2
6900  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6901  * please contact Symisc Systems via:
6902  *       legal@symisc.net
6903  *       licensing@symisc.net
6904  *       contact@symisc.net
6905  * or visit:
6906  *      http://jx9.symisc.net/
6907  */
6908  /* $SymiscID: api.c v1.7 FreeBSD 2012-12-18 06:54 stable <chm@symisc.net> $ */
6909 #ifndef JX9_AMALGAMATION
6910 #include "jx9Int.h"
6911 #endif
6912 /* This file implement the public interfaces presented to host-applications.
6913  * Routines in other files are for internal use by JX9 and should not be
6914  * accessed by users of the library.
6915  */
6916 #define JX9_ENGINE_MAGIC 0xF874BCD7
6917 #define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC)
6918 #define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
6919 /* If another thread have released a working instance, the following macros
6920  * evaluates to true. These macros are only used when the library
6921  * is built with threading support enabled which is not the case in
6922  * the default built.
6923  */
6924 #define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC)
6925 #define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
6926 /* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */
6927 /*
6928  * All global variables are collected in the structure named "sJx9MPGlobal".
6929  * That way it is clear in the code when we are using static variable because
6930  * its name start with sJx9MPGlobal.
6931  */
6932 static struct Jx9Global_Data
6933 {
6934 	SyMemBackend sAllocator;                /* Global low level memory allocator */
6935 #if defined(JX9_ENABLE_THREADS)
6936 	const SyMutexMethods *pMutexMethods;   /* Mutex methods */
6937 	SyMutex *pMutex;                       /* Global mutex */
6938 	sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded
6939 										    * The threading level can be set using the [jx9_lib_config()]
6940 											* interface with a configuration verb set to
6941 											* JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or
6942 											* JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
6943 											*/
6944 #endif
6945 	const jx9_vfs *pVfs;                    /* Underlying virtual file system */
6946 	sxi32 nEngine;                          /* Total number of active engines */
6947 	jx9 *pEngines;                          /* List of active engine */
6948 	sxu32 nMagic;                           /* Sanity check against library misuse */
6949 }sJx9MPGlobal = {
6950 	{0, 0, 0, 0, 0, 0, 0, 0, {0}},
6951 #if defined(JX9_ENABLE_THREADS)
6952 	0,
6953 	0,
6954 	0,
6955 #endif
6956 	0,
6957 	0,
6958 	0,
6959 	0
6960 };
6961 #define JX9_LIB_MAGIC  0xEA1495BA
6962 #define JX9_LIB_MISUSE (sJx9MPGlobal.nMagic != JX9_LIB_MAGIC)
6963 /*
6964  * Supported threading level.
6965  * These options have meaning only when the library is compiled with multi-threading
6966  * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined
6967  * when JX9 is built.
6968  * JX9_THREAD_LEVEL_SINGLE:
6969  * In this mode, mutexing is disabled and the library can only be used by a single thread.
6970  * JX9_THREAD_LEVEL_MULTI
6971  * In this mode, all mutexes including the recursive mutexes on [jx9] objects
6972  * are enabled so that the application is free to share the same engine
6973  * between different threads at the same time.
6974  */
6975 #define JX9_THREAD_LEVEL_SINGLE 1
6976 #define JX9_THREAD_LEVEL_MULTI  2
6977 /*
6978  * Configure a running JX9 engine instance.
6979  * return JX9_OK on success.Any other return
6980  * value indicates failure.
6981  * Refer to [jx9_config()].
6982  */
jx9EngineConfig(jx9 * pEngine,sxi32 nOp,va_list ap)6983 JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap)
6984 {
6985 	jx9_conf *pConf = &pEngine->xConf;
6986 	int rc = JX9_OK;
6987 	/* Perform the requested operation */
6988 	switch(nOp){
6989 	case JX9_CONFIG_ERR_LOG:{
6990 		/* Extract compile-time error log if any */
6991 		const char **pzPtr = va_arg(ap, const char **);
6992 		int *pLen = va_arg(ap, int *);
6993 		if( pzPtr == 0 ){
6994 			rc = JX9_CORRUPT;
6995 			break;
6996 		}
6997 		/* NULL terminate the error-log buffer */
6998 		SyBlobNullAppend(&pConf->sErrConsumer);
6999 		/* Point to the error-log buffer */
7000 		*pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer);
7001 		if( pLen ){
7002 			if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){
7003 				*pLen = (int)SyBlobLength(&pConf->sErrConsumer);
7004 			}else{
7005 				*pLen = 0;
7006 			}
7007 		}
7008 		break;
7009 							}
7010 	case JX9_CONFIG_ERR_ABORT:
7011 		/* Reserved for future use */
7012 		break;
7013 	default:
7014 		/* Unknown configuration verb */
7015 		rc = JX9_CORRUPT;
7016 		break;
7017 	} /* Switch() */
7018 	return rc;
7019 }
7020 /*
7021  * Configure the JX9 library.
7022  * Return JX9_OK on success. Any other return value indicates failure.
7023  * Refer to [jx9_lib_config()].
7024  */
Jx9CoreConfigure(sxi32 nOp,va_list ap)7025 static sxi32 Jx9CoreConfigure(sxi32 nOp, va_list ap)
7026 {
7027 	int rc = JX9_OK;
7028 	switch(nOp){
7029 	    case JX9_LIB_CONFIG_VFS:{
7030 			/* Install a virtual file system */
7031 			const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *);
7032 			sJx9MPGlobal.pVfs = pVfs;
7033 			break;
7034 								}
7035 		case JX9_LIB_CONFIG_USER_MALLOC: {
7036 			/* Use an alternative low-level memory allocation routines */
7037 			const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
7038 			/* Save the memory failure callback (if available) */
7039 			ProcMemError xMemErr = sJx9MPGlobal.sAllocator.xMemError;
7040 			void *pMemErr = sJx9MPGlobal.sAllocator.pUserData;
7041 			if( pMethods == 0 ){
7042 				/* Use the built-in memory allocation subsystem */
7043 				rc = SyMemBackendInit(&sJx9MPGlobal.sAllocator, xMemErr, pMemErr);
7044 			}else{
7045 				rc = SyMemBackendInitFromOthers(&sJx9MPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
7046 			}
7047 			break;
7048 										  }
7049 		case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: {
7050 			/* Memory failure callback */
7051 			ProcMemError xMemErr = va_arg(ap, ProcMemError);
7052 			void *pUserData = va_arg(ap, void *);
7053 			sJx9MPGlobal.sAllocator.xMemError = xMemErr;
7054 			sJx9MPGlobal.sAllocator.pUserData = pUserData;
7055 			break;
7056 												 }
7057 		case JX9_LIB_CONFIG_USER_MUTEX: {
7058 #if defined(JX9_ENABLE_THREADS)
7059 			/* Use an alternative low-level mutex subsystem */
7060 			const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
7061 #if defined (UNTRUST)
7062 			if( pMethods == 0 ){
7063 				rc = JX9_CORRUPT;
7064 			}
7065 #endif
7066 			/* Sanity check */
7067 			if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
7068 				/* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
7069 				rc = JX9_CORRUPT;
7070 				break;
7071 			}
7072 			if( sJx9MPGlobal.pMutexMethods ){
7073 				/* Overwrite the previous mutex subsystem */
7074 				SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
7075 				if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
7076 					sJx9MPGlobal.pMutexMethods->xGlobalRelease();
7077 				}
7078 				sJx9MPGlobal.pMutex = 0;
7079 			}
7080 			/* Initialize and install the new mutex subsystem */
7081 			if( pMethods->xGlobalInit ){
7082 				rc = pMethods->xGlobalInit();
7083 				if ( rc != JX9_OK ){
7084 					break;
7085 				}
7086 			}
7087 			/* Create the global mutex */
7088 			sJx9MPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
7089 			if( sJx9MPGlobal.pMutex == 0 ){
7090 				/*
7091 				 * If the supplied mutex subsystem is so sick that we are unable to
7092 				 * create a single mutex, there is no much we can do here.
7093 				 */
7094 				if( pMethods->xGlobalRelease ){
7095 					pMethods->xGlobalRelease();
7096 				}
7097 				rc = JX9_CORRUPT;
7098 				break;
7099 			}
7100 			sJx9MPGlobal.pMutexMethods = pMethods;
7101 			if( sJx9MPGlobal.nThreadingLevel == 0 ){
7102 				/* Set a default threading level */
7103 				sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
7104 			}
7105 #endif
7106 			break;
7107 										   }
7108 		case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE:
7109 #if defined(JX9_ENABLE_THREADS)
7110 			/* Single thread mode(Only one thread is allowed to play with the library) */
7111 			sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE;
7112 #endif
7113 			break;
7114 		case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI:
7115 #if defined(JX9_ENABLE_THREADS)
7116 			/* Multi-threading mode (library is thread safe and JX9 engines and virtual machines
7117 			 * may be shared between multiple threads).
7118 			 */
7119 			sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
7120 #endif
7121 			break;
7122 		default:
7123 			/* Unknown configuration option */
7124 			rc = JX9_CORRUPT;
7125 			break;
7126 	}
7127 	return rc;
7128 }
7129 /*
7130  * [CAPIREF: jx9_lib_config()]
7131  * Please refer to the official documentation for function purpose and expected parameters.
7132  */
jx9_lib_config(int nConfigOp,...)7133 JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...)
7134 {
7135 	va_list ap;
7136 	int rc;
7137 	if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
7138 		/* Library is already initialized, this operation is forbidden */
7139 		return JX9_LOOKED;
7140 	}
7141 	va_start(ap, nConfigOp);
7142 	rc = Jx9CoreConfigure(nConfigOp, ap);
7143 	va_end(ap);
7144 	return rc;
7145 }
7146 /*
7147  * Global library initialization
7148  * Refer to [jx9_lib_init()]
7149  * This routine must be called to initialize the memory allocation subsystem, the mutex
7150  * subsystem prior to doing any serious work with the library.The first thread to call
7151  * this routine does the initialization process and set the magic number so no body later
7152  * can re-initialize the library.If subsequent threads call this  routine before the first
7153  * thread have finished the initialization process, then the subsequent threads must block
7154  * until the initialization process is done.
7155  */
Jx9CoreInitialize(void)7156 static sxi32 Jx9CoreInitialize(void)
7157 {
7158 	const jx9_vfs *pVfs; /* Built-in vfs */
7159 #if defined(JX9_ENABLE_THREADS)
7160 	const SyMutexMethods *pMutexMethods = 0;
7161 	SyMutex *pMaster = 0;
7162 #endif
7163 	int rc;
7164 	/*
7165 	 * If the library is already initialized, then a call to this routine
7166 	 * is a no-op.
7167 	 */
7168 	if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
7169 		return JX9_OK; /* Already initialized */
7170 	}
7171 	/* Point to the built-in vfs */
7172 	pVfs = jx9ExportBuiltinVfs();
7173 	/* Install it */
7174 	jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs);
7175 #if defined(JX9_ENABLE_THREADS)
7176 	if( sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){
7177 		pMutexMethods = sJx9MPGlobal.pMutexMethods;
7178 		if( pMutexMethods == 0 ){
7179 			/* Use the built-in mutex subsystem */
7180 			pMutexMethods = SyMutexExportMethods();
7181 			if( pMutexMethods == 0 ){
7182 				return JX9_CORRUPT; /* Can't happen */
7183 			}
7184 			/* Install the mutex subsystem */
7185 			rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods);
7186 			if( rc != JX9_OK ){
7187 				return rc;
7188 			}
7189 		}
7190 		/* Obtain a static mutex so we can initialize the library without calling malloc() */
7191 		pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
7192 		if( pMaster == 0 ){
7193 			return JX9_CORRUPT; /* Can't happen */
7194 		}
7195 	}
7196 	/* Lock the master mutex */
7197 	rc = JX9_OK;
7198 	SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7199 	if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
7200 #endif
7201 		if( sJx9MPGlobal.sAllocator.pMethods == 0 ){
7202 			/* Install a memory subsystem */
7203 			rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
7204 			if( rc != JX9_OK ){
7205 				/* If we are unable to initialize the memory backend, there is no much we can do here.*/
7206 				goto End;
7207 			}
7208 		}
7209 #if defined(JX9_ENABLE_THREADS)
7210 		if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7211 			/* Protect the memory allocation subsystem */
7212 			rc = SyMemBackendMakeThreadSafe(&sJx9MPGlobal.sAllocator, sJx9MPGlobal.pMutexMethods);
7213 			if( rc != JX9_OK ){
7214 				goto End;
7215 			}
7216 		}
7217 #endif
7218 		/* Our library is initialized, set the magic number */
7219 		sJx9MPGlobal.nMagic = JX9_LIB_MAGIC;
7220 		rc = JX9_OK;
7221 #if defined(JX9_ENABLE_THREADS)
7222 	} /* sJx9MPGlobal.nMagic != JX9_LIB_MAGIC */
7223 #endif
7224 End:
7225 #if defined(JX9_ENABLE_THREADS)
7226 	/* Unlock the master mutex */
7227 	SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7228 #endif
7229 	return rc;
7230 }
7231 /*
7232  * Release an active JX9 engine and it's associated active virtual machines.
7233  */
EngineRelease(jx9 * pEngine)7234 static sxi32 EngineRelease(jx9 *pEngine)
7235 {
7236 	jx9_vm *pVm, *pNext;
7237 	/* Release all active VM */
7238 	pVm = pEngine->pVms;
7239 	for(;;){
7240 		if( pEngine->iVm < 1 ){
7241 			break;
7242 		}
7243 		pNext = pVm->pNext;
7244 		jx9VmRelease(pVm);
7245 		pVm = pNext;
7246 		pEngine->iVm--;
7247 	}
7248 	/* Set a dummy magic number */
7249 	pEngine->nMagic = 0x7635;
7250 	/* Release the private memory subsystem */
7251 	SyMemBackendRelease(&pEngine->sAllocator);
7252 	return JX9_OK;
7253 }
7254 /*
7255  * Release all resources consumed by the library.
7256  * If JX9 is already shut when this routine is invoked then this
7257  * routine is a harmless no-op.
7258  * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()].
7259  */
JX9CoreShutdown(void)7260 static void JX9CoreShutdown(void)
7261 {
7262 	jx9 *pEngine, *pNext;
7263 	/* Release all active engines first */
7264 	pEngine = sJx9MPGlobal.pEngines;
7265 	for(;;){
7266 		if( sJx9MPGlobal.nEngine < 1 ){
7267 			break;
7268 		}
7269 		pNext = pEngine->pNext;
7270 		EngineRelease(pEngine);
7271 		pEngine = pNext;
7272 		sJx9MPGlobal.nEngine--;
7273 	}
7274 #if defined(JX9_ENABLE_THREADS)
7275 	/* Release the mutex subsystem */
7276 	if( sJx9MPGlobal.pMutexMethods ){
7277 		if( sJx9MPGlobal.pMutex ){
7278 			SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
7279 			sJx9MPGlobal.pMutex = 0;
7280 		}
7281 		if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
7282 			sJx9MPGlobal.pMutexMethods->xGlobalRelease();
7283 		}
7284 		sJx9MPGlobal.pMutexMethods = 0;
7285 	}
7286 	sJx9MPGlobal.nThreadingLevel = 0;
7287 #endif
7288 	if( sJx9MPGlobal.sAllocator.pMethods ){
7289 		/* Release the memory backend */
7290 		SyMemBackendRelease(&sJx9MPGlobal.sAllocator);
7291 	}
7292 	sJx9MPGlobal.nMagic = 0x1928;
7293 }
7294 /*
7295  * [CAPIREF: jx9_lib_shutdown()]
7296  * Please refer to the official documentation for function purpose and expected parameters.
7297  */
jx9_lib_shutdown(void)7298 JX9_PRIVATE int jx9_lib_shutdown(void)
7299 {
7300 	if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
7301 		/* Already shut */
7302 		return JX9_OK;
7303 	}
7304 	JX9CoreShutdown();
7305 	return JX9_OK;
7306 }
7307 /*
7308  * [CAPIREF: jx9_lib_signature()]
7309  * Please refer to the official documentation for function purpose and expected parameters.
7310  */
jx9_lib_signature(void)7311 JX9_PRIVATE const char * jx9_lib_signature(void)
7312 {
7313 	return JX9_SIG;
7314 }
7315 /*
7316  * [CAPIREF: jx9_init()]
7317  * Please refer to the official documentation for function purpose and expected parameters.
7318  */
jx9_init(jx9 ** ppEngine)7319 JX9_PRIVATE int jx9_init(jx9 **ppEngine)
7320 {
7321 	jx9 *pEngine;
7322 	int rc;
7323 #if defined(UNTRUST)
7324 	if( ppEngine == 0 ){
7325 		return JX9_CORRUPT;
7326 	}
7327 #endif
7328 	*ppEngine = 0;
7329 	/* One-time automatic library initialization */
7330 	rc = Jx9CoreInitialize();
7331 	if( rc != JX9_OK ){
7332 		return rc;
7333 	}
7334 	/* Allocate a new engine */
7335 	pEngine = (jx9 *)SyMemBackendPoolAlloc(&sJx9MPGlobal.sAllocator, sizeof(jx9));
7336 	if( pEngine == 0 ){
7337 		return JX9_NOMEM;
7338 	}
7339 	/* Zero the structure */
7340 	SyZero(pEngine, sizeof(jx9));
7341 	/* Initialize engine fields */
7342 	pEngine->nMagic = JX9_ENGINE_MAGIC;
7343 	rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sJx9MPGlobal.sAllocator);
7344 	if( rc != JX9_OK ){
7345 		goto Release;
7346 	}
7347 #if defined(JX9_ENABLE_THREADS)
7348 	SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
7349 #endif
7350 	/* Default configuration */
7351 	SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
7352 	/* Install a default compile-time error consumer routine */
7353 	pEngine->xConf.xErr = jx9VmBlobConsumer;
7354 	pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer;
7355 	/* Built-in vfs */
7356 	pEngine->pVfs = sJx9MPGlobal.pVfs;
7357 #if defined(JX9_ENABLE_THREADS)
7358 	if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7359 		 /* Associate a recursive mutex with this instance */
7360 		 pEngine->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
7361 		 if( pEngine->pMutex == 0 ){
7362 			 rc = JX9_NOMEM;
7363 			 goto Release;
7364 		 }
7365 	 }
7366 #endif
7367 	/* Link to the list of active engines */
7368 #if defined(JX9_ENABLE_THREADS)
7369 	/* Enter the global mutex */
7370 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7371 #endif
7372 	MACRO_LD_PUSH(sJx9MPGlobal.pEngines, pEngine);
7373 	sJx9MPGlobal.nEngine++;
7374 #if defined(JX9_ENABLE_THREADS)
7375 	/* Leave the global mutex */
7376 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7377 #endif
7378 	/* Write a pointer to the new instance */
7379 	*ppEngine = pEngine;
7380 	return JX9_OK;
7381 Release:
7382 	SyMemBackendRelease(&pEngine->sAllocator);
7383 	SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator,pEngine);
7384 	return rc;
7385 }
7386 /*
7387  * [CAPIREF: jx9_release()]
7388  * Please refer to the official documentation for function purpose and expected parameters.
7389  */
jx9_release(jx9 * pEngine)7390 JX9_PRIVATE int jx9_release(jx9 *pEngine)
7391 {
7392 	int rc;
7393 	if( JX9_ENGINE_MISUSE(pEngine) ){
7394 		return JX9_CORRUPT;
7395 	}
7396 #if defined(JX9_ENABLE_THREADS)
7397 	 /* Acquire engine mutex */
7398 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7399 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7400 		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7401 			 return JX9_ABORT; /* Another thread have released this instance */
7402 	 }
7403 #endif
7404 	/* Release the engine */
7405 	rc = EngineRelease(&(*pEngine));
7406 #if defined(JX9_ENABLE_THREADS)
7407 	 /* Leave engine mutex */
7408 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7409 	 /* Release engine mutex */
7410 	 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7411 #endif
7412 #if defined(JX9_ENABLE_THREADS)
7413 	/* Enter the global mutex */
7414 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7415 #endif
7416 	/* Unlink from the list of active engines */
7417 	MACRO_LD_REMOVE(sJx9MPGlobal.pEngines, pEngine);
7418 	sJx9MPGlobal.nEngine--;
7419 #if defined(JX9_ENABLE_THREADS)
7420 	/* Leave the global mutex */
7421 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
7422 #endif
7423 	/* Release the memory chunk allocated to this engine */
7424 	SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator, pEngine);
7425 	return rc;
7426 }
7427 /*
7428  * Compile a raw JX9 script.
7429  * To execute a JX9 code, it must first be compiled into a bytecode program using this routine.
7430  * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback]
7431  * should  display the appropriate error message and this function set ppVm to null and return
7432  * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled
7433  * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.].
7434  * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script
7435  * for evaluation.
7436  */
ProcessScript(jx9 * pEngine,jx9_vm ** ppVm,SyString * pScript,sxi32 iFlags,const char * zFilePath)7437 static sxi32 ProcessScript(
7438 	jx9 *pEngine,          /* Running JX9 engine */
7439 	jx9_vm **ppVm,         /* OUT: A pointer to the virtual machine */
7440 	SyString *pScript,     /* Raw JX9 script to compile */
7441 	sxi32 iFlags,          /* Compile-time flags */
7442 	const char *zFilePath  /* File path if script come from a file. NULL otherwise */
7443 	)
7444 {
7445 	jx9_vm *pVm;
7446 	int rc;
7447 	/* Allocate a new virtual machine */
7448 	pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm));
7449 	if( pVm == 0 ){
7450 		/* If the supplied memory subsystem is so sick that we are unable to allocate
7451 		 * a tiny chunk of memory, there is no much we can do here. */
7452 		if( ppVm ){
7453 			*ppVm = 0;
7454 		}
7455 		return JX9_NOMEM;
7456 	}
7457 	if( iFlags < 0 ){
7458 		/* Default compile-time flags */
7459 		iFlags = 0;
7460 	}
7461 	/* Initialize the Virtual Machine */
7462 	rc = jx9VmInit(pVm, &(*pEngine));
7463 	if( rc != JX9_OK ){
7464 		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7465 		if( ppVm ){
7466 			*ppVm = 0;
7467 		}
7468 		return JX9_VM_ERR;
7469 	}
7470 	if( zFilePath ){
7471 		/* Push processed file path */
7472 		jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0);
7473 	}
7474 	/* Reset the error message consumer */
7475 	SyBlobReset(&pEngine->xConf.sErrConsumer);
7476 	/* Compile the script */
7477 	jx9CompileScript(pVm, &(*pScript), iFlags);
7478 	if( pVm->sCodeGen.nErr > 0 || pVm == 0){
7479 		sxu32 nErr = pVm->sCodeGen.nErr;
7480 		/* Compilation error or null ppVm pointer, release this VM */
7481 		SyMemBackendRelease(&pVm->sAllocator);
7482 		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7483 		if( ppVm ){
7484 			*ppVm = 0;
7485 		}
7486 		return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK;
7487 	}
7488 	/* Prepare the virtual machine for bytecode execution */
7489 	rc = jx9VmMakeReady(pVm);
7490 	if( rc != JX9_OK ){
7491 		goto Release;
7492 	}
7493 	/* Install local import path which is the current directory */
7494 	jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./");
7495 #if defined(JX9_ENABLE_THREADS)
7496 	if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
7497 		 /* Associate a recursive mutex with this instance */
7498 		 pVm->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
7499 		 if( pVm->pMutex == 0 ){
7500 			 goto Release;
7501 		 }
7502 	 }
7503 #endif
7504 	/* Script successfully compiled, link to the list of active virtual machines */
7505 	MACRO_LD_PUSH(pEngine->pVms, pVm);
7506 	pEngine->iVm++;
7507 	/* Point to the freshly created VM */
7508 	*ppVm = pVm;
7509 	/* Ready to execute JX9 bytecode */
7510 	return JX9_OK;
7511 Release:
7512 	SyMemBackendRelease(&pVm->sAllocator);
7513 	SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7514 	*ppVm = 0;
7515 	return JX9_VM_ERR;
7516 }
7517 /*
7518  * [CAPIREF: jx9_compile()]
7519  * Please refer to the official documentation for function purpose and expected parameters.
7520  */
jx9_compile(jx9 * pEngine,const char * zSource,int nLen,jx9_vm ** ppOutVm)7521 JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm)
7522 {
7523 	SyString sScript;
7524 	int rc;
7525 	if( JX9_ENGINE_MISUSE(pEngine) ){
7526 		return JX9_CORRUPT;
7527 	}
7528 	if( zSource == 0 ){
7529 		/* Empty Jx9 statement ';' */
7530 		zSource = ";";
7531 		nLen = (int)sizeof(char);
7532 	}
7533 	if( nLen < 0 ){
7534 		/* Compute input length automatically */
7535 		nLen = (int)SyStrlen(zSource);
7536 	}
7537 	SyStringInitFromBuf(&sScript, zSource, nLen);
7538 #if defined(JX9_ENABLE_THREADS)
7539 	 /* Acquire engine mutex */
7540 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7541 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7542 		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7543 			 return JX9_ABORT; /* Another thread have released this instance */
7544 	 }
7545 #endif
7546 	/* Compile the script */
7547 	rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0);
7548 #if defined(JX9_ENABLE_THREADS)
7549 	 /* Leave engine mutex */
7550 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7551 #endif
7552 	/* Compilation result */
7553 	return rc;
7554 }
7555 /*
7556  * [CAPIREF: jx9_compile_file()]
7557  * Please refer to the official documentation for function purpose and expected parameters.
7558  */
jx9_compile_file(jx9 * pEngine,const char * zFilePath,jx9_vm ** ppOutVm)7559 JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm)
7560 {
7561 	const jx9_vfs *pVfs;
7562 	int rc;
7563 	if( ppOutVm ){
7564 		*ppOutVm = 0;
7565 	}
7566 	rc = JX9_OK; /* cc warning */
7567 	if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){
7568 		return JX9_CORRUPT;
7569 	}
7570 #if defined(JX9_ENABLE_THREADS)
7571 	 /* Acquire engine mutex */
7572 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7573 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7574 		 JX9_THRD_ENGINE_RELEASE(pEngine) ){
7575 			 return JX9_ABORT; /* Another thread have released this instance */
7576 	 }
7577 #endif
7578 	 /*
7579 	  * Check if the underlying vfs implement the memory map
7580 	  * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
7581 	  */
7582 	 pVfs = pEngine->pVfs;
7583 	 if( pVfs == 0 || pVfs->xMmap == 0 ){
7584 		 /* Memory map routine not implemented */
7585 		 rc = JX9_IO_ERR;
7586 	 }else{
7587 		 void *pMapView = 0; /* cc warning */
7588 		 jx9_int64 nSize = 0; /* cc warning */
7589 		 SyString sScript;
7590 		 /* Try to get a memory view of the whole file */
7591 		 rc = pVfs->xMmap(zFilePath, &pMapView, &nSize);
7592 		 if( rc != JX9_OK ){
7593 			 /* Assume an IO error */
7594 			 rc = JX9_IO_ERR;
7595 		 }else{
7596 			 /* Compile the file */
7597 			 SyStringInitFromBuf(&sScript, pMapView, nSize);
7598 			 rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath);
7599 			 /* Release the memory view of the whole file */
7600 			 if( pVfs->xUnmap ){
7601 				 pVfs->xUnmap(pMapView, nSize);
7602 			 }
7603 		 }
7604 	 }
7605 #if defined(JX9_ENABLE_THREADS)
7606 	 /* Leave engine mutex */
7607 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7608 #endif
7609 	/* Compilation result */
7610 	return rc;
7611 }
7612 /*
7613  * [CAPIREF: jx9_vm_config()]
7614  * Please refer to the official documentation for function purpose and expected parameters.
7615  */
jx9_vm_config(jx9_vm * pVm,int iConfigOp,...)7616 JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...)
7617 {
7618 	va_list ap;
7619 	int rc;
7620 	/* Ticket 1433-002: NULL VM is harmless operation */
7621 	if ( JX9_VM_MISUSE(pVm) ){
7622 		return JX9_CORRUPT;
7623 	}
7624 #if defined(JX9_ENABLE_THREADS)
7625 	 /* Acquire VM mutex */
7626 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7627 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7628 		 JX9_THRD_VM_RELEASE(pVm) ){
7629 			 return JX9_ABORT; /* Another thread have released this instance */
7630 	 }
7631 #endif
7632 	/* Confiugure the virtual machine */
7633 	va_start(ap, iConfigOp);
7634 	rc = jx9VmConfigure(&(*pVm), iConfigOp, ap);
7635 	va_end(ap);
7636 #if defined(JX9_ENABLE_THREADS)
7637 	 /* Leave VM mutex */
7638 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7639 #endif
7640 	return rc;
7641 }
7642 /*
7643  * [CAPIREF: jx9_vm_release()]
7644  * Please refer to the official documentation for function purpose and expected parameters.
7645  */
jx9_vm_release(jx9_vm * pVm)7646 JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm)
7647 {
7648 	jx9 *pEngine;
7649 	int rc;
7650 	/* Ticket 1433-002: NULL VM is harmless operation */
7651 	if ( JX9_VM_MISUSE(pVm) ){
7652 		return JX9_CORRUPT;
7653 	}
7654 #if defined(JX9_ENABLE_THREADS)
7655 	 /* Acquire VM mutex */
7656 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7657 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7658 		 JX9_THRD_VM_RELEASE(pVm) ){
7659 			 return JX9_ABORT; /* Another thread have released this instance */
7660 	 }
7661 #endif
7662 	pEngine = pVm->pEngine;
7663 	rc = jx9VmRelease(&(*pVm));
7664 #if defined(JX9_ENABLE_THREADS)
7665 	 /* Leave VM mutex */
7666 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7667 	 /* Release VM mutex */
7668 	 SyMutexRelease(sJx9MPGlobal.pMutexMethods, pVm->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7669 #endif
7670 	if( rc == JX9_OK ){
7671 		/* Unlink from the list of active VM */
7672 #if defined(JX9_ENABLE_THREADS)
7673 			/* Acquire engine mutex */
7674 			SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7675 			if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7676 				JX9_THRD_ENGINE_RELEASE(pEngine) ){
7677 					return JX9_ABORT; /* Another thread have released this instance */
7678 			}
7679 #endif
7680 		MACRO_LD_REMOVE(pEngine->pVms, pVm);
7681 		pEngine->iVm--;
7682 		/* Release the memory chunk allocated to this VM */
7683 		SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
7684 #if defined(JX9_ENABLE_THREADS)
7685 			/* Leave engine mutex */
7686 			SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7687 #endif
7688 	}
7689 	return rc;
7690 }
7691 /*
7692  * [CAPIREF: jx9_create_function()]
7693  * Please refer to the official documentation for function purpose and expected parameters.
7694  */
jx9_create_function(jx9_vm * pVm,const char * zName,int (* xFunc)(jx9_context *,int,jx9_value **),void * pUserData)7695 JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData)
7696 {
7697 	SyString sName;
7698 	int rc;
7699 	/* Ticket 1433-002: NULL VM is harmless operation */
7700 	if ( JX9_VM_MISUSE(pVm) ){
7701 		return JX9_CORRUPT;
7702 	}
7703 	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
7704 	/* Remove leading and trailing white spaces */
7705 	SyStringFullTrim(&sName);
7706 	/* Ticket 1433-003: NULL values are not allowed */
7707 	if( sName.nByte < 1 || xFunc == 0 ){
7708 		return JX9_CORRUPT;
7709 	}
7710 #if defined(JX9_ENABLE_THREADS)
7711 	 /* Acquire VM mutex */
7712 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7713 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7714 		 JX9_THRD_VM_RELEASE(pVm) ){
7715 			 return JX9_ABORT; /* Another thread have released this instance */
7716 	 }
7717 #endif
7718 	/* Install the foreign function */
7719 	rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData);
7720 #if defined(JX9_ENABLE_THREADS)
7721 	 /* Leave VM mutex */
7722 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7723 #endif
7724 	return rc;
7725 }
jx9DeleteFunction(jx9_vm * pVm,const char * zName)7726 JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName)
7727 {
7728 	jx9_user_func *pFunc = 0; /* cc warning */
7729 	int rc;
7730 	/* Perform the deletion */
7731 	rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
7732 	if( rc == JX9_OK ){
7733 		/* Release internal fields */
7734 		SySetRelease(&pFunc->aAux);
7735 		SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
7736 		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
7737 	}
7738 	return rc;
7739 }
7740 /*
7741  * [CAPIREF: jx9_create_constant()]
7742  * Please refer to the official documentation for function purpose and expected parameters.
7743  */
jx9_create_constant(jx9_vm * pVm,const char * zName,void (* xExpand)(jx9_value *,void *),void * pUserData)7744 JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData)
7745 {
7746 	SyString sName;
7747 	int rc;
7748 	/* Ticket 1433-002: NULL VM is harmless operation */
7749 	if ( JX9_VM_MISUSE(pVm) ){
7750 		return JX9_CORRUPT;
7751 	}
7752 	SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
7753 	/* Remove leading and trailing white spaces */
7754 	SyStringFullTrim(&sName);
7755 	if( sName.nByte < 1 ){
7756 		/* Empty constant name */
7757 		return JX9_CORRUPT;
7758 	}
7759 	/* TICKET 1433-003: NULL pointer is harmless operation */
7760 	if( xExpand == 0 ){
7761 		return JX9_CORRUPT;
7762 	}
7763 #if defined(JX9_ENABLE_THREADS)
7764 	 /* Acquire VM mutex */
7765 	 SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7766 	 if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
7767 		 JX9_THRD_VM_RELEASE(pVm) ){
7768 			 return JX9_ABORT; /* Another thread have released this instance */
7769 	 }
7770 #endif
7771 	/* Perform the registration */
7772 	rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData);
7773 #if defined(JX9_ENABLE_THREADS)
7774 	 /* Leave VM mutex */
7775 	 SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
7776 #endif
7777 	 return rc;
7778 }
Jx9DeleteConstant(jx9_vm * pVm,const char * zName)7779 JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName)
7780 {
7781 	jx9_constant *pCons;
7782 	int rc;
7783 	/* Query the constant hashtable */
7784 	 rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
7785 	 if( rc == JX9_OK ){
7786 		 /* Perform the deletion */
7787 		 SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
7788 		 SyMemBackendPoolFree(&pVm->sAllocator, pCons);
7789 	 }
7790 	 return rc;
7791 }
7792 /*
7793  * [CAPIREF: jx9_new_scalar()]
7794  * Please refer to the official documentation for function purpose and expected parameters.
7795  */
jx9_new_scalar(jx9_vm * pVm)7796 JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm)
7797 {
7798 	jx9_value *pObj;
7799 	/* Ticket 1433-002: NULL VM is harmless operation */
7800 	if ( JX9_VM_MISUSE(pVm) ){
7801 		return 0;
7802 	}
7803 	/* Allocate a new scalar variable */
7804 	pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
7805 	if( pObj == 0 ){
7806 		return 0;
7807 	}
7808 	/* Nullify the new scalar */
7809 	jx9MemObjInit(pVm, pObj);
7810 	return pObj;
7811 }
7812 /*
7813  * [CAPIREF: jx9_new_array()]
7814  * Please refer to the official documentation for function purpose and expected parameters.
7815  */
jx9_new_array(jx9_vm * pVm)7816 JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm)
7817 {
7818 	jx9_hashmap *pMap;
7819 	jx9_value *pObj;
7820 	/* Ticket 1433-002: NULL VM is harmless operation */
7821 	if ( JX9_VM_MISUSE(pVm) ){
7822 		return 0;
7823 	}
7824 	/* Create a new hashmap first */
7825 	pMap = jx9NewHashmap(&(*pVm), 0, 0);
7826 	if( pMap == 0 ){
7827 		return 0;
7828 	}
7829 	/* Associate a new jx9_value with this hashmap */
7830 	pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
7831 	if( pObj == 0 ){
7832 		jx9HashmapRelease(pMap, TRUE);
7833 		return 0;
7834 	}
7835 	jx9MemObjInitFromArray(pVm, pObj, pMap);
7836 	return pObj;
7837 }
7838 /*
7839  * [CAPIREF: jx9_release_value()]
7840  * Please refer to the official documentation for function purpose and expected parameters.
7841  */
jx9_release_value(jx9_vm * pVm,jx9_value * pValue)7842 JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue)
7843 {
7844 	/* Ticket 1433-002: NULL VM is a harmless operation */
7845 	if ( JX9_VM_MISUSE(pVm) ){
7846 		return JX9_CORRUPT;
7847 	}
7848 	if( pValue ){
7849 		/* Release the value */
7850 		jx9MemObjRelease(pValue);
7851 		SyMemBackendPoolFree(&pVm->sAllocator, pValue);
7852 	}
7853 	return JX9_OK;
7854 }
7855 /*
7856  * [CAPIREF: jx9_value_to_int()]
7857  * Please refer to the official documentation for function purpose and expected parameters.
7858  */
jx9_value_to_int(jx9_value * pValue)7859 JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue)
7860 {
7861 	int rc;
7862 	rc = jx9MemObjToInteger(pValue);
7863 	if( rc != JX9_OK ){
7864 		return 0;
7865 	}
7866 	return (int)pValue->x.iVal;
7867 }
7868 /*
7869  * [CAPIREF: jx9_value_to_bool()]
7870  * Please refer to the official documentation for function purpose and expected parameters.
7871  */
jx9_value_to_bool(jx9_value * pValue)7872 JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue)
7873 {
7874 	int rc;
7875 	rc = jx9MemObjToBool(pValue);
7876 	if( rc != JX9_OK ){
7877 		return 0;
7878 	}
7879 	return (int)pValue->x.iVal;
7880 }
7881 /*
7882  * [CAPIREF: jx9_value_to_int64()]
7883  * Please refer to the official documentation for function purpose and expected parameters.
7884  */
jx9_value_to_int64(jx9_value * pValue)7885 JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue)
7886 {
7887 	int rc;
7888 	rc = jx9MemObjToInteger(pValue);
7889 	if( rc != JX9_OK ){
7890 		return 0;
7891 	}
7892 	return pValue->x.iVal;
7893 }
7894 /*
7895  * [CAPIREF: jx9_value_to_double()]
7896  * Please refer to the official documentation for function purpose and expected parameters.
7897  */
jx9_value_to_double(jx9_value * pValue)7898 JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue)
7899 {
7900 	int rc;
7901 	rc = jx9MemObjToReal(pValue);
7902 	if( rc != JX9_OK ){
7903 		return (double)0;
7904 	}
7905 	return (double)pValue->x.rVal;
7906 }
7907 /*
7908  * [CAPIREF: jx9_value_to_string()]
7909  * Please refer to the official documentation for function purpose and expected parameters.
7910  */
jx9_value_to_string(jx9_value * pValue,int * pLen)7911 JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen)
7912 {
7913 	jx9MemObjToString(pValue);
7914 	if( SyBlobLength(&pValue->sBlob) > 0 ){
7915 		SyBlobNullAppend(&pValue->sBlob);
7916 		if( pLen ){
7917 			*pLen = (int)SyBlobLength(&pValue->sBlob);
7918 		}
7919 		return (const char *)SyBlobData(&pValue->sBlob);
7920 	}else{
7921 		/* Return the empty string */
7922 		if( pLen ){
7923 			*pLen = 0;
7924 		}
7925 		return "";
7926 	}
7927 }
7928 /*
7929  * [CAPIREF: jx9_value_to_resource()]
7930  * Please refer to the official documentation for function purpose and expected parameters.
7931  */
jx9_value_to_resource(jx9_value * pValue)7932 JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue)
7933 {
7934 	if( (pValue->iFlags & MEMOBJ_RES) == 0 ){
7935 		/* Not a resource, return NULL */
7936 		return 0;
7937 	}
7938 	return pValue->x.pOther;
7939 }
7940 /*
7941  * [CAPIREF: jx9_value_compare()]
7942  * Please refer to the official documentation for function purpose and expected parameters.
7943  */
jx9_value_compare(jx9_value * pLeft,jx9_value * pRight,int bStrict)7944 JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict)
7945 {
7946 	int rc;
7947 	if( pLeft == 0 || pRight == 0 ){
7948 		/* TICKET 1433-24: NULL values is harmless operation */
7949 		return 1;
7950 	}
7951 	/* Perform the comparison */
7952 	rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0);
7953 	/* Comparison result */
7954 	return rc;
7955 }
7956 /*
7957  * [CAPIREF: jx9_result_int()]
7958  * Please refer to the official documentation for function purpose and expected parameters.
7959  */
jx9_result_int(jx9_context * pCtx,int iValue)7960 JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue)
7961 {
7962 	return jx9_value_int(pCtx->pRet, iValue);
7963 }
7964 /*
7965  * [CAPIREF: jx9_result_int64()]
7966  * Please refer to the official documentation for function purpose and expected parameters.
7967  */
jx9_result_int64(jx9_context * pCtx,jx9_int64 iValue)7968 JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue)
7969 {
7970 	return jx9_value_int64(pCtx->pRet, iValue);
7971 }
7972 /*
7973  * [CAPIREF: jx9_result_bool()]
7974  * Please refer to the official documentation for function purpose and expected parameters.
7975  */
jx9_result_bool(jx9_context * pCtx,int iBool)7976 JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool)
7977 {
7978 	return jx9_value_bool(pCtx->pRet, iBool);
7979 }
7980 /*
7981  * [CAPIREF: jx9_result_double()]
7982  * Please refer to the official documentation for function purpose and expected parameters.
7983  */
jx9_result_double(jx9_context * pCtx,double Value)7984 JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value)
7985 {
7986 	return jx9_value_double(pCtx->pRet, Value);
7987 }
7988 /*
7989  * [CAPIREF: jx9_result_null()]
7990  * Please refer to the official documentation for function purpose and expected parameters.
7991  */
jx9_result_null(jx9_context * pCtx)7992 JX9_PRIVATE int jx9_result_null(jx9_context *pCtx)
7993 {
7994 	/* Invalidate any prior representation and set the NULL flag */
7995 	jx9MemObjRelease(pCtx->pRet);
7996 	return JX9_OK;
7997 }
7998 /*
7999  * [CAPIREF: jx9_result_string()]
8000  * Please refer to the official documentation for function purpose and expected parameters.
8001  */
jx9_result_string(jx9_context * pCtx,const char * zString,int nLen)8002 JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen)
8003 {
8004 	return jx9_value_string(pCtx->pRet, zString, nLen);
8005 }
8006 /*
8007  * [CAPIREF: jx9_result_string_format()]
8008  * Please refer to the official documentation for function purpose and expected parameters.
8009  */
jx9_result_string_format(jx9_context * pCtx,const char * zFormat,...)8010 JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...)
8011 {
8012 	jx9_value *p;
8013 	va_list ap;
8014 	int rc;
8015 	p = pCtx->pRet;
8016 	if( (p->iFlags & MEMOBJ_STRING) == 0 ){
8017 		/* Invalidate any prior representation */
8018 		jx9MemObjRelease(p);
8019 		MemObjSetType(p, MEMOBJ_STRING);
8020 	}
8021 	/* Format the given string */
8022 	va_start(ap, zFormat);
8023 	rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
8024 	va_end(ap);
8025 	return rc;
8026 }
8027 /*
8028  * [CAPIREF: jx9_result_value()]
8029  * Please refer to the official documentation for function purpose and expected parameters.
8030  */
jx9_result_value(jx9_context * pCtx,jx9_value * pValue)8031 JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue)
8032 {
8033 	int rc = JX9_OK;
8034 	if( pValue == 0 ){
8035 		jx9MemObjRelease(pCtx->pRet);
8036 	}else{
8037 		rc = jx9MemObjStore(pValue, pCtx->pRet);
8038 	}
8039 	return rc;
8040 }
8041 /*
8042  * [CAPIREF: jx9_result_resource()]
8043  * Please refer to the official documentation for function purpose and expected parameters.
8044  */
jx9_result_resource(jx9_context * pCtx,void * pUserData)8045 JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData)
8046 {
8047 	return jx9_value_resource(pCtx->pRet, pUserData);
8048 }
8049 /*
8050  * [CAPIREF: jx9_context_new_scalar()]
8051  * Please refer to the official documentation for function purpose and expected parameters.
8052  */
jx9_context_new_scalar(jx9_context * pCtx)8053 JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx)
8054 {
8055 	jx9_value *pVal;
8056 	pVal = jx9_new_scalar(pCtx->pVm);
8057 	if( pVal ){
8058 		/* Record value address so it can be freed automatically
8059 		 * when the calling function returns.
8060 		 */
8061 		SySetPut(&pCtx->sVar, (const void *)&pVal);
8062 	}
8063 	return pVal;
8064 }
8065 /*
8066  * [CAPIREF: jx9_context_new_array()]
8067  * Please refer to the official documentation for function purpose and expected parameters.
8068  */
jx9_context_new_array(jx9_context * pCtx)8069 JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx)
8070 {
8071 	jx9_value *pVal;
8072 	pVal = jx9_new_array(pCtx->pVm);
8073 	if( pVal ){
8074 		/* Record value address so it can be freed automatically
8075 		 * when the calling function returns.
8076 		 */
8077 		SySetPut(&pCtx->sVar, (const void *)&pVal);
8078 	}
8079 	return pVal;
8080 }
8081 /*
8082  * [CAPIREF: jx9_context_release_value()]
8083  * Please refer to the official documentation for function purpose and expected parameters.
8084  */
jx9_context_release_value(jx9_context * pCtx,jx9_value * pValue)8085 JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue)
8086 {
8087 	jx9VmReleaseContextValue(&(*pCtx), pValue);
8088 }
8089 /*
8090  * [CAPIREF: jx9_context_alloc_chunk()]
8091  * Please refer to the official documentation for function purpose and expected parameters.
8092  */
jx9_context_alloc_chunk(jx9_context * pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)8093 JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease)
8094 {
8095 	void *pChunk;
8096 	pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte);
8097 	if( pChunk ){
8098 		if( ZeroChunk ){
8099 			/* Zero the memory chunk */
8100 			SyZero(pChunk, nByte);
8101 		}
8102 		if( AutoRelease ){
8103 			jx9_aux_data sAux;
8104 			/* Track the chunk so that it can be released automatically
8105 			 * upon this context is destroyed.
8106 			 */
8107 			sAux.pAuxData = pChunk;
8108 			SySetPut(&pCtx->sChunk, (const void *)&sAux);
8109 		}
8110 	}
8111 	return pChunk;
8112 }
8113 /*
8114  * Check if the given chunk address is registered in the call context
8115  * chunk container.
8116  * Return TRUE if registered.FALSE otherwise.
8117  * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()].
8118  */
ContextFindChunk(jx9_context * pCtx,void * pChunk)8119 static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk)
8120 {
8121 	jx9_aux_data *aAux, *pAux;
8122 	sxu32 n;
8123 	if( SySetUsed(&pCtx->sChunk) < 1 ){
8124 		/* Don't bother processing, the container is empty */
8125 		return 0;
8126 	}
8127 	/* Perform the lookup */
8128 	aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
8129 	for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
8130 		pAux = &aAux[n];
8131 		if( pAux->pAuxData == pChunk ){
8132 			/* Chunk found */
8133 			return pAux;
8134 		}
8135 	}
8136 	/* No such allocated chunk */
8137 	return 0;
8138 }
8139 /*
8140  * [CAPIREF: jx9_context_realloc_chunk()]
8141  * Please refer to the official documentation for function purpose and expected parameters.
8142  */
jx9_context_realloc_chunk(jx9_context * pCtx,void * pChunk,unsigned int nByte)8143 JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte)
8144 {
8145 	jx9_aux_data *pAux;
8146 	void *pNew;
8147 	pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte);
8148 	if( pNew ){
8149 		pAux = ContextFindChunk(pCtx, pChunk);
8150 		if( pAux ){
8151 			pAux->pAuxData = pNew;
8152 		}
8153 	}
8154 	return pNew;
8155 }
8156 /*
8157  * [CAPIREF: jx9_context_free_chunk()]
8158  * Please refer to the official documentation for function purpose and expected parameters.
8159  */
jx9_context_free_chunk(jx9_context * pCtx,void * pChunk)8160 JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk)
8161 {
8162 	jx9_aux_data *pAux;
8163 	if( pChunk == 0 ){
8164 		/* TICKET-1433-93: NULL chunk is a harmless operation */
8165 		return;
8166 	}
8167 	pAux = ContextFindChunk(pCtx, pChunk);
8168 	if( pAux ){
8169 		/* Mark as destroyed */
8170 		pAux->pAuxData = 0;
8171 	}
8172 	SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
8173 }
8174 /*
8175  * [CAPIREF: jx9_array_fetch()]
8176  * Please refer to the official documentation for function purpose and expected parameters.
8177  */
jx9_array_fetch(jx9_value * pArray,const char * zKey,int nByte)8178 JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte)
8179 {
8180 	jx9_hashmap_node *pNode;
8181 	jx9_value *pValue;
8182 	jx9_value skey;
8183 	int rc;
8184 	/* Make sure we are dealing with a valid hashmap */
8185 	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8186 		return 0;
8187 	}
8188 	if( nByte < 0 ){
8189 		nByte = (int)SyStrlen(zKey);
8190 	}
8191 	/* Convert the key to a jx9_value  */
8192 	jx9MemObjInit(pArray->pVm, &skey);
8193 	jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte);
8194 	/* Perform the lookup */
8195 	rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode);
8196 	jx9MemObjRelease(&skey);
8197 	if( rc != JX9_OK ){
8198 		/* No such entry */
8199 		return 0;
8200 	}
8201 	/* Extract the target value */
8202 	pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx);
8203 	return pValue;
8204 }
8205 /*
8206  * [CAPIREF: jx9_array_walk()]
8207  * Please refer to the official documentation for function purpose and expected parameters.
8208  */
jx9_array_walk(jx9_value * pArray,int (* xWalk)(jx9_value * pValue,jx9_value *,void *),void * pUserData)8209 JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData)
8210 {
8211 	int rc;
8212 	if( xWalk == 0 ){
8213 		return JX9_CORRUPT;
8214 	}
8215 	/* Make sure we are dealing with a valid hashmap */
8216 	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8217 		return JX9_CORRUPT;
8218 	}
8219 	/* Start the walk process */
8220 	rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData);
8221 	return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK;
8222 }
8223 /*
8224  * [CAPIREF: jx9_array_add_elem()]
8225  * Please refer to the official documentation for function purpose and expected parameters.
8226  */
jx9_array_add_elem(jx9_value * pArray,jx9_value * pKey,jx9_value * pValue)8227 JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue)
8228 {
8229 	int rc;
8230 	/* Make sure we are dealing with a valid hashmap */
8231 	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8232 		return JX9_CORRUPT;
8233 	}
8234 	/* Perform the insertion */
8235 	rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue));
8236 	return rc;
8237 }
8238 /*
8239  * [CAPIREF: jx9_array_add_strkey_elem()]
8240  * Please refer to the official documentation for function purpose and expected parameters.
8241  */
jx9_array_add_strkey_elem(jx9_value * pArray,const char * zKey,jx9_value * pValue)8242 JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue)
8243 {
8244 	int rc;
8245 	/* Make sure we are dealing with a valid hashmap */
8246 	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8247 		return JX9_CORRUPT;
8248 	}
8249 	/* Perform the insertion */
8250 	if( SX_EMPTY_STR(zKey) ){
8251 		/* Empty key, assign an automatic index */
8252 		rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue));
8253 	}else{
8254 		jx9_value sKey;
8255 		jx9MemObjInitFromString(pArray->pVm, &sKey, 0);
8256 		jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey));
8257 		rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue));
8258 		jx9MemObjRelease(&sKey);
8259 	}
8260 	return rc;
8261 }
8262 /*
8263  * [CAPIREF: jx9_array_count()]
8264  * Please refer to the official documentation for function purpose and expected parameters.
8265  */
jx9_array_count(jx9_value * pArray)8266 JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray)
8267 {
8268 	jx9_hashmap *pMap;
8269 	/* Make sure we are dealing with a valid hashmap */
8270 	if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
8271 		return 0;
8272 	}
8273 	/* Point to the internal representation of the hashmap */
8274 	pMap = (jx9_hashmap *)pArray->x.pOther;
8275 	return pMap->nEntry;
8276 }
8277 /*
8278  * [CAPIREF: jx9_context_output()]
8279  * Please refer to the official documentation for function purpose and expected parameters.
8280  */
jx9_context_output(jx9_context * pCtx,const char * zString,int nLen)8281 JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen)
8282 {
8283 	SyString sData;
8284 	int rc;
8285 	if( nLen < 0 ){
8286 		nLen = (int)SyStrlen(zString);
8287 	}
8288 	SyStringInitFromBuf(&sData, zString, nLen);
8289 	rc = jx9VmOutputConsume(pCtx->pVm, &sData);
8290 	return rc;
8291 }
8292 /*
8293  * [CAPIREF: jx9_context_throw_error()]
8294  * Please refer to the official documentation for function purpose and expected parameters.
8295  */
jx9_context_throw_error(jx9_context * pCtx,int iErr,const char * zErr)8296 JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr)
8297 {
8298 	int rc = JX9_OK;
8299 	if( zErr ){
8300 		rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr);
8301 	}
8302 	return rc;
8303 }
8304 /*
8305  * [CAPIREF: jx9_context_throw_error_format()]
8306  * Please refer to the official documentation for function purpose and expected parameters.
8307  */
jx9_context_throw_error_format(jx9_context * pCtx,int iErr,const char * zFormat,...)8308 JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...)
8309 {
8310 	va_list ap;
8311 	int rc;
8312 	if( zFormat == 0){
8313 		return JX9_OK;
8314 	}
8315 	va_start(ap, zFormat);
8316 	rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
8317 	va_end(ap);
8318 	return rc;
8319 }
8320 /*
8321  * [CAPIREF: jx9_context_random_num()]
8322  * Please refer to the official documentation for function purpose and expected parameters.
8323  */
jx9_context_random_num(jx9_context * pCtx)8324 JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx)
8325 {
8326 	sxu32 n;
8327 	n = jx9VmRandomNum(pCtx->pVm);
8328 	return n;
8329 }
8330 /*
8331  * [CAPIREF: jx9_context_random_string()]
8332  * Please refer to the official documentation for function purpose and expected parameters.
8333  */
jx9_context_random_string(jx9_context * pCtx,char * zBuf,int nBuflen)8334 JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen)
8335 {
8336 	if( nBuflen < 3 ){
8337 		return JX9_CORRUPT;
8338 	}
8339 	jx9VmRandomString(pCtx->pVm, zBuf, nBuflen);
8340 	return JX9_OK;
8341 }
8342 /*
8343  * [CAPIREF: jx9_context_user_data()]
8344  * Please refer to the official documentation for function purpose and expected parameters.
8345  */
jx9_context_user_data(jx9_context * pCtx)8346 JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx)
8347 {
8348 	return pCtx->pFunc->pUserData;
8349 }
8350 /*
8351  * [CAPIREF: jx9_context_push_aux_data()]
8352  * Please refer to the official documentation for function purpose and expected parameters.
8353  */
jx9_context_push_aux_data(jx9_context * pCtx,void * pUserData)8354 JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData)
8355 {
8356 	jx9_aux_data sAux;
8357 	int rc;
8358 	sAux.pAuxData = pUserData;
8359 	rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux);
8360 	return rc;
8361 }
8362 /*
8363  * [CAPIREF: jx9_context_peek_aux_data()]
8364  * Please refer to the official documentation for function purpose and expected parameters.
8365  */
jx9_context_peek_aux_data(jx9_context * pCtx)8366 JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx)
8367 {
8368 	jx9_aux_data *pAux;
8369 	pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux);
8370 	return pAux ? pAux->pAuxData : 0;
8371 }
8372 /*
8373  * [CAPIREF: jx9_context_pop_aux_data()]
8374  * Please refer to the official documentation for function purpose and expected parameters.
8375  */
jx9_context_pop_aux_data(jx9_context * pCtx)8376 JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx)
8377 {
8378 	jx9_aux_data *pAux;
8379 	pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux);
8380 	return pAux ? pAux->pAuxData : 0;
8381 }
8382 /*
8383  * [CAPIREF: jx9_context_result_buf_length()]
8384  * Please refer to the official documentation for function purpose and expected parameters.
8385  */
jx9_context_result_buf_length(jx9_context * pCtx)8386 JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx)
8387 {
8388 	return SyBlobLength(&pCtx->pRet->sBlob);
8389 }
8390 /*
8391  * [CAPIREF: jx9_function_name()]
8392  * Please refer to the official documentation for function purpose and expected parameters.
8393  */
jx9_function_name(jx9_context * pCtx)8394 JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx)
8395 {
8396 	SyString *pName;
8397 	pName = &pCtx->pFunc->sName;
8398 	return pName->zString;
8399 }
8400 /*
8401  * [CAPIREF: jx9_value_int()]
8402  * Please refer to the official documentation for function purpose and expected parameters.
8403  */
jx9_value_int(jx9_value * pVal,int iValue)8404 JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue)
8405 {
8406 	/* Invalidate any prior representation */
8407 	jx9MemObjRelease(pVal);
8408 	pVal->x.iVal = (jx9_int64)iValue;
8409 	MemObjSetType(pVal, MEMOBJ_INT);
8410 	return JX9_OK;
8411 }
8412 /*
8413  * [CAPIREF: jx9_value_int64()]
8414  * Please refer to the official documentation for function purpose and expected parameters.
8415  */
jx9_value_int64(jx9_value * pVal,jx9_int64 iValue)8416 JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue)
8417 {
8418 	/* Invalidate any prior representation */
8419 	jx9MemObjRelease(pVal);
8420 	pVal->x.iVal = iValue;
8421 	MemObjSetType(pVal, MEMOBJ_INT);
8422 	return JX9_OK;
8423 }
8424 /*
8425  * [CAPIREF: jx9_value_bool()]
8426  * Please refer to the official documentation for function purpose and expected parameters.
8427  */
jx9_value_bool(jx9_value * pVal,int iBool)8428 JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool)
8429 {
8430 	/* Invalidate any prior representation */
8431 	jx9MemObjRelease(pVal);
8432 	pVal->x.iVal = iBool ? 1 : 0;
8433 	MemObjSetType(pVal, MEMOBJ_BOOL);
8434 	return JX9_OK;
8435 }
8436 /*
8437  * [CAPIREF: jx9_value_null()]
8438  * Please refer to the official documentation for function purpose and expected parameters.
8439  */
jx9_value_null(jx9_value * pVal)8440 JX9_PRIVATE int jx9_value_null(jx9_value *pVal)
8441 {
8442 	/* Invalidate any prior representation and set the NULL flag */
8443 	jx9MemObjRelease(pVal);
8444 	return JX9_OK;
8445 }
8446 /*
8447  * [CAPIREF: jx9_value_double()]
8448  * Please refer to the official documentation for function purpose and expected parameters.
8449  */
jx9_value_double(jx9_value * pVal,double Value)8450 JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value)
8451 {
8452 	/* Invalidate any prior representation */
8453 	jx9MemObjRelease(pVal);
8454 	pVal->x.rVal = (jx9_real)Value;
8455 	MemObjSetType(pVal, MEMOBJ_REAL);
8456 	/* Try to get an integer representation also */
8457 	jx9MemObjTryInteger(pVal);
8458 	return JX9_OK;
8459 }
8460 /*
8461  * [CAPIREF: jx9_value_string()]
8462  * Please refer to the official documentation for function purpose and expected parameters.
8463  */
jx9_value_string(jx9_value * pVal,const char * zString,int nLen)8464 JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen)
8465 {
8466 	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
8467 		/* Invalidate any prior representation */
8468 		jx9MemObjRelease(pVal);
8469 		MemObjSetType(pVal, MEMOBJ_STRING);
8470 	}
8471 	if( zString ){
8472 		if( nLen < 0 ){
8473 			/* Compute length automatically */
8474 			nLen = (int)SyStrlen(zString);
8475 		}
8476 		SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen);
8477 	}
8478 	return JX9_OK;
8479 }
8480 /*
8481  * [CAPIREF: jx9_value_string_format()]
8482  * Please refer to the official documentation for function purpose and expected parameters.
8483  */
jx9_value_string_format(jx9_value * pVal,const char * zFormat,...)8484 JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...)
8485 {
8486 	va_list ap;
8487 	int rc;
8488 	if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
8489 		/* Invalidate any prior representation */
8490 		jx9MemObjRelease(pVal);
8491 		MemObjSetType(pVal, MEMOBJ_STRING);
8492 	}
8493 	va_start(ap, zFormat);
8494 	rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
8495 	va_end(ap);
8496 	return JX9_OK;
8497 }
8498 /*
8499  * [CAPIREF: jx9_value_reset_string_cursor()]
8500  * Please refer to the official documentation for function purpose and expected parameters.
8501  */
jx9_value_reset_string_cursor(jx9_value * pVal)8502 JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal)
8503 {
8504 	/* Reset the string cursor */
8505 	SyBlobReset(&pVal->sBlob);
8506 	return JX9_OK;
8507 }
8508 /*
8509  * [CAPIREF: jx9_value_resource()]
8510  * Please refer to the official documentation for function purpose and expected parameters.
8511  */
jx9_value_resource(jx9_value * pVal,void * pUserData)8512 JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData)
8513 {
8514 	/* Invalidate any prior representation */
8515 	jx9MemObjRelease(pVal);
8516 	/* Reflect the new type */
8517 	pVal->x.pOther = pUserData;
8518 	MemObjSetType(pVal, MEMOBJ_RES);
8519 	return JX9_OK;
8520 }
8521 /*
8522  * [CAPIREF: jx9_value_release()]
8523  * Please refer to the official documentation for function purpose and expected parameters.
8524  */
jx9_value_release(jx9_value * pVal)8525 JX9_PRIVATE int jx9_value_release(jx9_value *pVal)
8526 {
8527 	jx9MemObjRelease(pVal);
8528 	return JX9_OK;
8529 }
8530 /*
8531  * [CAPIREF: jx9_value_is_int()]
8532  * Please refer to the official documentation for function purpose and expected parameters.
8533  */
jx9_value_is_int(jx9_value * pVal)8534 JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal)
8535 {
8536 	return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE;
8537 }
8538 /*
8539  * [CAPIREF: jx9_value_is_float()]
8540  * Please refer to the official documentation for function purpose and expected parameters.
8541  */
jx9_value_is_float(jx9_value * pVal)8542 JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal)
8543 {
8544 	return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE;
8545 }
8546 /*
8547  * [CAPIREF: jx9_value_is_bool()]
8548  * Please refer to the official documentation for function purpose and expected parameters.
8549  */
jx9_value_is_bool(jx9_value * pVal)8550 JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal)
8551 {
8552 	return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE;
8553 }
8554 /*
8555  * [CAPIREF: jx9_value_is_string()]
8556  * Please refer to the official documentation for function purpose and expected parameters.
8557  */
jx9_value_is_string(jx9_value * pVal)8558 JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal)
8559 {
8560 	return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE;
8561 }
8562 /*
8563  * [CAPIREF: jx9_value_is_null()]
8564  * Please refer to the official documentation for function purpose and expected parameters.
8565  */
jx9_value_is_null(jx9_value * pVal)8566 JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal)
8567 {
8568 	return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE;
8569 }
8570 /*
8571  * [CAPIREF: jx9_value_is_numeric()]
8572  * Please refer to the official documentation for function purpose and expected parameters.
8573  */
jx9_value_is_numeric(jx9_value * pVal)8574 JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal)
8575 {
8576 	int rc;
8577 	rc = jx9MemObjIsNumeric(pVal);
8578 	return rc;
8579 }
8580 /*
8581  * [CAPIREF: jx9_value_is_callable()]
8582  * Please refer to the official documentation for function purpose and expected parameters.
8583  */
jx9_value_is_callable(jx9_value * pVal)8584 JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal)
8585 {
8586 	int rc;
8587 	rc = jx9VmIsCallable(pVal->pVm, pVal);
8588 	return rc;
8589 }
8590 /*
8591  * [CAPIREF: jx9_value_is_scalar()]
8592  * Please refer to the official documentation for function purpose and expected parameters.
8593  */
jx9_value_is_scalar(jx9_value * pVal)8594 JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal)
8595 {
8596 	return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE;
8597 }
8598 /*
8599  * [CAPIREF: jx9_value_is_json_array()]
8600  * Please refer to the official documentation for function purpose and expected parameters.
8601  */
jx9_value_is_json_array(jx9_value * pVal)8602 JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal)
8603 {
8604 	return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE;
8605 }
8606 /*
8607  * [CAPIREF: jx9_value_is_json_object()]
8608  * Please refer to the official documentation for function purpose and expected parameters.
8609  */
jx9_value_is_json_object(jx9_value * pVal)8610 JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal)
8611 {
8612 	jx9_hashmap *pMap;
8613 	if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){
8614 		return FALSE;
8615 	}
8616 	pMap = (jx9_hashmap *)pVal->x.pOther;
8617 	if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){
8618 		return FALSE;
8619 	}
8620 	return TRUE;
8621 }
8622 /*
8623  * [CAPIREF: jx9_value_is_resource()]
8624  * Please refer to the official documentation for function purpose and expected parameters.
8625  */
jx9_value_is_resource(jx9_value * pVal)8626 JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal)
8627 {
8628 	return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE;
8629 }
8630 /*
8631  * [CAPIREF: jx9_value_is_empty()]
8632  * Please refer to the official documentation for function purpose and expected parameters.
8633  */
jx9_value_is_empty(jx9_value * pVal)8634 JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal)
8635 {
8636 	int rc;
8637 	rc = jx9MemObjIsEmpty(pVal);
8638 	return rc;
8639 }
8640 
8641 /* jx9_builtin.c */
8642 /*
8643  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
8644  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
8645  * Version 1.7.2
8646  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
8647  * please contact Symisc Systems via:
8648  *       legal@symisc.net
8649  *       licensing@symisc.net
8650  *       contact@symisc.net
8651  * or visit:
8652  *      http://jx9.symisc.net/
8653  */
8654  /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
8655 #ifndef JX9_AMALGAMATION
8656 #include "jx9Int.h"
8657 #endif
8658 /* This file implement built-in 'foreign' functions for the JX9 engine */
8659 /*
8660  * Section:
8661  *    Variable handling Functions.
8662  * Authors:
8663  *    Symisc Systems, devel@symisc.net.
8664  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
8665  * Status:
8666  *    Stable.
8667  */
8668 /*
8669  * bool is_bool($var)
8670  *  Finds out whether a variable is a boolean.
8671  * Parameters
8672  *   $var: The variable being evaluated.
8673  * Return
8674  *  TRUE if var is a boolean. False otherwise.
8675  */
jx9Builtin_is_bool(jx9_context * pCtx,int nArg,jx9_value ** apArg)8676 static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
8677 {
8678 	int res = 0; /* Assume false by default */
8679 	if( nArg > 0 ){
8680 		res = jx9_value_is_bool(apArg[0]);
8681 	}
8682 	/* Query result */
8683 	jx9_result_bool(pCtx, res);
8684 	return JX9_OK;
8685 }
8686 /*
8687  * bool is_float($var)
8688  * bool is_real($var)
8689  * bool is_double($var)
8690  *  Finds out whether a variable is a float.
8691  * Parameters
8692  *   $var: The variable being evaluated.
8693  * Return
8694  *  TRUE if var is a float. False otherwise.
8695  */
jx9Builtin_is_float(jx9_context * pCtx,int nArg,jx9_value ** apArg)8696 static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
8697 {
8698 	int res = 0; /* Assume false by default */
8699 	if( nArg > 0 ){
8700 		res = jx9_value_is_float(apArg[0]);
8701 	}
8702 	/* Query result */
8703 	jx9_result_bool(pCtx, res);
8704 	return JX9_OK;
8705 }
8706 /*
8707  * bool is_int($var)
8708  * bool is_integer($var)
8709  * bool is_long($var)
8710  *  Finds out whether a variable is an integer.
8711  * Parameters
8712  *   $var: The variable being evaluated.
8713  * Return
8714  *  TRUE if var is an integer. False otherwise.
8715  */
jx9Builtin_is_int(jx9_context * pCtx,int nArg,jx9_value ** apArg)8716 static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
8717 {
8718 	int res = 0; /* Assume false by default */
8719 	if( nArg > 0 ){
8720 		res = jx9_value_is_int(apArg[0]);
8721 	}
8722 	/* Query result */
8723 	jx9_result_bool(pCtx, res);
8724 	return JX9_OK;
8725 }
8726 /*
8727  * bool is_string($var)
8728  *  Finds out whether a variable is a string.
8729  * Parameters
8730  *   $var: The variable being evaluated.
8731  * Return
8732  *  TRUE if var is string. False otherwise.
8733  */
jx9Builtin_is_string(jx9_context * pCtx,int nArg,jx9_value ** apArg)8734 static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
8735 {
8736 	int res = 0; /* Assume false by default */
8737 	if( nArg > 0 ){
8738 		res = jx9_value_is_string(apArg[0]);
8739 	}
8740 	/* Query result */
8741 	jx9_result_bool(pCtx, res);
8742 	return JX9_OK;
8743 }
8744 /*
8745  * bool is_null($var)
8746  *  Finds out whether a variable is NULL.
8747  * Parameters
8748  *   $var: The variable being evaluated.
8749  * Return
8750  *  TRUE if var is NULL. False otherwise.
8751  */
jx9Builtin_is_null(jx9_context * pCtx,int nArg,jx9_value ** apArg)8752 static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
8753 {
8754 	int res = 0; /* Assume false by default */
8755 	if( nArg > 0 ){
8756 		res = jx9_value_is_null(apArg[0]);
8757 	}
8758 	/* Query result */
8759 	jx9_result_bool(pCtx, res);
8760 	return JX9_OK;
8761 }
8762 /*
8763  * bool is_numeric($var)
8764  *  Find out whether a variable is NULL.
8765  * Parameters
8766  *  $var: The variable being evaluated.
8767  * Return
8768  *  True if var is numeric. False otherwise.
8769  */
jx9Builtin_is_numeric(jx9_context * pCtx,int nArg,jx9_value ** apArg)8770 static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
8771 {
8772 	int res = 0; /* Assume false by default */
8773 	if( nArg > 0 ){
8774 		res = jx9_value_is_numeric(apArg[0]);
8775 	}
8776 	/* Query result */
8777 	jx9_result_bool(pCtx, res);
8778 	return JX9_OK;
8779 }
8780 /*
8781  * bool is_scalar($var)
8782  *  Find out whether a variable is a scalar.
8783  * Parameters
8784  *  $var: The variable being evaluated.
8785  * Return
8786  *  True if var is scalar. False otherwise.
8787  */
jx9Builtin_is_scalar(jx9_context * pCtx,int nArg,jx9_value ** apArg)8788 static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
8789 {
8790 	int res = 0; /* Assume false by default */
8791 	if( nArg > 0 ){
8792 		res = jx9_value_is_scalar(apArg[0]);
8793 	}
8794 	/* Query result */
8795 	jx9_result_bool(pCtx, res);
8796 	return JX9_OK;
8797 }
8798 /*
8799  * bool is_array($var)
8800  *  Find out whether a variable is an array.
8801  * Parameters
8802  *  $var: The variable being evaluated.
8803  * Return
8804  *  True if var is an array. False otherwise.
8805  */
jx9Builtin_is_array(jx9_context * pCtx,int nArg,jx9_value ** apArg)8806 static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
8807 {
8808 	int res = 0; /* Assume false by default */
8809 	if( nArg > 0 ){
8810 		res = jx9_value_is_json_array(apArg[0]);
8811 	}
8812 	/* Query result */
8813 	jx9_result_bool(pCtx, res);
8814 	return JX9_OK;
8815 }
8816 /*
8817  * bool is_object($var)
8818  *  Find out whether a variable is an object.
8819  * Parameters
8820  *  $var: The variable being evaluated.
8821  * Return
8822  *  True if var is an object. False otherwise.
8823  */
jx9Builtin_is_object(jx9_context * pCtx,int nArg,jx9_value ** apArg)8824 static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
8825 {
8826 	int res = 0; /* Assume false by default */
8827 	if( nArg > 0 ){
8828 		res = jx9_value_is_json_object(apArg[0]);
8829 	}
8830 	/* Query result */
8831 	jx9_result_bool(pCtx, res);
8832 	return JX9_OK;
8833 }
8834 /*
8835  * bool is_resource($var)
8836  *  Find out whether a variable is a resource.
8837  * Parameters
8838  *  $var: The variable being evaluated.
8839  * Return
8840  *  True if a resource. False otherwise.
8841  */
jx9Builtin_is_resource(jx9_context * pCtx,int nArg,jx9_value ** apArg)8842 static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
8843 {
8844 	int res = 0; /* Assume false by default */
8845 	if( nArg > 0 ){
8846 		res = jx9_value_is_resource(apArg[0]);
8847 	}
8848 	jx9_result_bool(pCtx, res);
8849 	return JX9_OK;
8850 }
8851 /*
8852  * float floatval($var)
8853  *  Get float value of a variable.
8854  * Parameter
8855  *  $var: The variable being processed.
8856  * Return
8857  *  the float value of a variable.
8858  */
jx9Builtin_floatval(jx9_context * pCtx,int nArg,jx9_value ** apArg)8859 static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8860 {
8861 	if( nArg < 1 ){
8862 		/* return 0.0 */
8863 		jx9_result_double(pCtx, 0);
8864 	}else{
8865 		double dval;
8866 		/* Perform the cast */
8867 		dval = jx9_value_to_double(apArg[0]);
8868 		jx9_result_double(pCtx, dval);
8869 	}
8870 	return JX9_OK;
8871 }
8872 /*
8873  * int intval($var)
8874  *  Get integer value of a variable.
8875  * Parameter
8876  *  $var: The variable being processed.
8877  * Return
8878  *  the int value of a variable.
8879  */
jx9Builtin_intval(jx9_context * pCtx,int nArg,jx9_value ** apArg)8880 static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8881 {
8882 	if( nArg < 1 ){
8883 		/* return 0 */
8884 		jx9_result_int(pCtx, 0);
8885 	}else{
8886 		sxi64 iVal;
8887 		/* Perform the cast */
8888 		iVal = jx9_value_to_int64(apArg[0]);
8889 		jx9_result_int64(pCtx, iVal);
8890 	}
8891 	return JX9_OK;
8892 }
8893 /*
8894  * string strval($var)
8895  *  Get the string representation of a variable.
8896  * Parameter
8897  *  $var: The variable being processed.
8898  * Return
8899  *  the string value of a variable.
8900  */
jx9Builtin_strval(jx9_context * pCtx,int nArg,jx9_value ** apArg)8901 static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
8902 {
8903 	if( nArg < 1 ){
8904 		/* return NULL */
8905 		jx9_result_null(pCtx);
8906 	}else{
8907 		const char *zVal;
8908 		int iLen = 0; /* cc -O6 warning */
8909 		/* Perform the cast */
8910 		zVal = jx9_value_to_string(apArg[0], &iLen);
8911 		jx9_result_string(pCtx, zVal, iLen);
8912 	}
8913 	return JX9_OK;
8914 }
8915 /*
8916  * bool empty($var)
8917  *  Determine whether a variable is empty.
8918  * Parameters
8919  *   $var: The variable being checked.
8920  * Return
8921  *  0 if var has a non-empty and non-zero value.1 otherwise.
8922  */
jx9Builtin_empty(jx9_context * pCtx,int nArg,jx9_value ** apArg)8923 static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
8924 {
8925 	int res = 1; /* Assume empty by default */
8926 	if( nArg > 0 ){
8927 		res = jx9_value_is_empty(apArg[0]);
8928 	}
8929 	jx9_result_bool(pCtx, res);
8930 	return JX9_OK;
8931 
8932 }
8933 #ifndef JX9_DISABLE_BUILTIN_FUNC
8934 #ifdef JX9_ENABLE_MATH_FUNC
8935 /*
8936  * Section:
8937  *    Math Functions.
8938  * Authors:
8939  *    Symisc Systems, devel@symisc.net.
8940  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
8941  * Status:
8942  *    Stable.
8943  */
8944 #include <stdlib.h> /* abs */
8945 #include <math.h>
8946 /*
8947  * float sqrt(float $arg )
8948  *  Square root of the given number.
8949  * Parameter
8950  *  The number to process.
8951  * Return
8952  *  The square root of arg or the special value Nan of failure.
8953  */
jx9Builtin_sqrt(jx9_context * pCtx,int nArg,jx9_value ** apArg)8954 static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
8955 {
8956 	double r, x;
8957 	if( nArg < 1 ){
8958 		/* Missing argument, return 0 */
8959 		jx9_result_int(pCtx, 0);
8960 		return JX9_OK;
8961 	}
8962 	x = jx9_value_to_double(apArg[0]);
8963 	/* Perform the requested operation */
8964 	r = sqrt(x);
8965 	/* store the result back */
8966 	jx9_result_double(pCtx, r);
8967 	return JX9_OK;
8968 }
8969 /*
8970  * float exp(float $arg )
8971  *  Calculates the exponent of e.
8972  * Parameter
8973  *  The number to process.
8974  * Return
8975  *  'e' raised to the power of arg.
8976  */
jx9Builtin_exp(jx9_context * pCtx,int nArg,jx9_value ** apArg)8977 static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
8978 {
8979 	double r, x;
8980 	if( nArg < 1 ){
8981 		/* Missing argument, return 0 */
8982 		jx9_result_int(pCtx, 0);
8983 		return JX9_OK;
8984 	}
8985 	x = jx9_value_to_double(apArg[0]);
8986 	/* Perform the requested operation */
8987 	r = exp(x);
8988 	/* store the result back */
8989 	jx9_result_double(pCtx, r);
8990 	return JX9_OK;
8991 }
8992 /*
8993  * float floor(float $arg )
8994  *  Round fractions down.
8995  * Parameter
8996  *  The number to process.
8997  * Return
8998  *  Returns the next lowest integer value by rounding down value if necessary.
8999  */
jx9Builtin_floor(jx9_context * pCtx,int nArg,jx9_value ** apArg)9000 static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
9001 {
9002 	double r, x;
9003 	if( nArg < 1 ){
9004 		/* Missing argument, return 0 */
9005 		jx9_result_int(pCtx, 0);
9006 		return JX9_OK;
9007 	}
9008 	x = jx9_value_to_double(apArg[0]);
9009 	/* Perform the requested operation */
9010 	r = floor(x);
9011 	/* store the result back */
9012 	jx9_result_double(pCtx, r);
9013 	return JX9_OK;
9014 }
9015 /*
9016  * float cos(float $arg )
9017  *  Cosine.
9018  * Parameter
9019  *  The number to process.
9020  * Return
9021  *  The cosine of arg.
9022  */
jx9Builtin_cos(jx9_context * pCtx,int nArg,jx9_value ** apArg)9023 static int jx9Builtin_cos(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 = cos(x);
9034 	/* store the result back */
9035 	jx9_result_double(pCtx, r);
9036 	return JX9_OK;
9037 }
9038 /*
9039  * float acos(float $arg )
9040  *  Arc cosine.
9041  * Parameter
9042  *  The number to process.
9043  * Return
9044  *  The arc cosine of arg.
9045  */
jx9Builtin_acos(jx9_context * pCtx,int nArg,jx9_value ** apArg)9046 static int jx9Builtin_acos(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 = acos(x);
9057 	/* store the result back */
9058 	jx9_result_double(pCtx, r);
9059 	return JX9_OK;
9060 }
9061 /*
9062  * float cosh(float $arg )
9063  *  Hyperbolic cosine.
9064  * Parameter
9065  *  The number to process.
9066  * Return
9067  *  The hyperbolic cosine of arg.
9068  */
jx9Builtin_cosh(jx9_context * pCtx,int nArg,jx9_value ** apArg)9069 static int jx9Builtin_cosh(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 = cosh(x);
9080 	/* store the result back */
9081 	jx9_result_double(pCtx, r);
9082 	return JX9_OK;
9083 }
9084 /*
9085  * float sin(float $arg )
9086  *  Sine.
9087  * Parameter
9088  *  The number to process.
9089  * Return
9090  *  The sine of arg.
9091  */
jx9Builtin_sin(jx9_context * pCtx,int nArg,jx9_value ** apArg)9092 static int jx9Builtin_sin(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 = sin(x);
9103 	/* store the result back */
9104 	jx9_result_double(pCtx, r);
9105 	return JX9_OK;
9106 }
9107 /*
9108  * float asin(float $arg )
9109  *  Arc sine.
9110  * Parameter
9111  *  The number to process.
9112  * Return
9113  *  The arc sine of arg.
9114  */
jx9Builtin_asin(jx9_context * pCtx,int nArg,jx9_value ** apArg)9115 static int jx9Builtin_asin(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 = asin(x);
9126 	/* store the result back */
9127 	jx9_result_double(pCtx, r);
9128 	return JX9_OK;
9129 }
9130 /*
9131  * float sinh(float $arg )
9132  *  Hyperbolic sine.
9133  * Parameter
9134  *  The number to process.
9135  * Return
9136  *  The hyperbolic sine of arg.
9137  */
jx9Builtin_sinh(jx9_context * pCtx,int nArg,jx9_value ** apArg)9138 static int jx9Builtin_sinh(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 = sinh(x);
9149 	/* store the result back */
9150 	jx9_result_double(pCtx, r);
9151 	return JX9_OK;
9152 }
9153 /*
9154  * float ceil(float $arg )
9155  *  Round fractions up.
9156  * Parameter
9157  *  The number to process.
9158  * Return
9159  *  The next highest integer value by rounding up value if necessary.
9160  */
jx9Builtin_ceil(jx9_context * pCtx,int nArg,jx9_value ** apArg)9161 static int jx9Builtin_ceil(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 = ceil(x);
9172 	/* store the result back */
9173 	jx9_result_double(pCtx, r);
9174 	return JX9_OK;
9175 }
9176 /*
9177  * float tan(float $arg )
9178  *  Tangent.
9179  * Parameter
9180  *  The number to process.
9181  * Return
9182  *  The tangent of arg.
9183  */
jx9Builtin_tan(jx9_context * pCtx,int nArg,jx9_value ** apArg)9184 static int jx9Builtin_tan(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 = tan(x);
9195 	/* store the result back */
9196 	jx9_result_double(pCtx, r);
9197 	return JX9_OK;
9198 }
9199 /*
9200  * float atan(float $arg )
9201  *  Arc tangent.
9202  * Parameter
9203  *  The number to process.
9204  * Return
9205  *  The arc tangent of arg.
9206  */
jx9Builtin_atan(jx9_context * pCtx,int nArg,jx9_value ** apArg)9207 static int jx9Builtin_atan(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 = atan(x);
9218 	/* store the result back */
9219 	jx9_result_double(pCtx, r);
9220 	return JX9_OK;
9221 }
9222 /*
9223  * float tanh(float $arg )
9224  *  Hyperbolic tangent.
9225  * Parameter
9226  *  The number to process.
9227  * Return
9228  *  The Hyperbolic tangent of arg.
9229  */
jx9Builtin_tanh(jx9_context * pCtx,int nArg,jx9_value ** apArg)9230 static int jx9Builtin_tanh(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 = tanh(x);
9241 	/* store the result back */
9242 	jx9_result_double(pCtx, r);
9243 	return JX9_OK;
9244 }
9245 /*
9246  * float atan2(float $y, float $x)
9247  *  Arc tangent of two variable.
9248  * Parameter
9249  *  $y = Dividend parameter.
9250  *  $x = Divisor parameter.
9251  * Return
9252  *  The arc tangent of y/x in radian.
9253  */
jx9Builtin_atan2(jx9_context * pCtx,int nArg,jx9_value ** apArg)9254 static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
9255 {
9256 	double r, x, y;
9257 	if( nArg < 2 ){
9258 		/* Missing arguments, return 0 */
9259 		jx9_result_int(pCtx, 0);
9260 		return JX9_OK;
9261 	}
9262 	y = jx9_value_to_double(apArg[0]);
9263 	x = jx9_value_to_double(apArg[1]);
9264 	/* Perform the requested operation */
9265 	r = atan2(y, x);
9266 	/* store the result back */
9267 	jx9_result_double(pCtx, r);
9268 	return JX9_OK;
9269 }
9270 /*
9271  * float/int64 abs(float/int64 $arg )
9272  *  Absolute value.
9273  * Parameter
9274  *  The number to process.
9275  * Return
9276  *  The absolute value of number.
9277  */
jx9Builtin_abs(jx9_context * pCtx,int nArg,jx9_value ** apArg)9278 static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
9279 {
9280 	int is_float;
9281 	if( nArg < 1 ){
9282 		/* Missing argument, return 0 */
9283 		jx9_result_int(pCtx, 0);
9284 		return JX9_OK;
9285 	}
9286 	is_float = jx9_value_is_float(apArg[0]);
9287 	if( is_float ){
9288 		double r, x;
9289 		x = jx9_value_to_double(apArg[0]);
9290 		/* Perform the requested operation */
9291 		r = fabs(x);
9292 		jx9_result_double(pCtx, r);
9293 	}else{
9294 		int r, x;
9295 		x = jx9_value_to_int(apArg[0]);
9296 		/* Perform the requested operation */
9297 		r = abs(x);
9298 		jx9_result_int(pCtx, r);
9299 	}
9300 	return JX9_OK;
9301 }
9302 /*
9303  * float log(float $arg, [int/float $base])
9304  *  Natural logarithm.
9305  * Parameter
9306  *  $arg: The number to process.
9307  *  $base: The optional logarithmic base to use. (only base-10 is supported)
9308  * Return
9309  *  The logarithm of arg to base, if given, or the natural logarithm.
9310  * Note:
9311  *  only Natural log and base-10 log are supported.
9312  */
jx9Builtin_log(jx9_context * pCtx,int nArg,jx9_value ** apArg)9313 static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
9314 {
9315 	double r, x;
9316 	if( nArg < 1 ){
9317 		/* Missing argument, return 0 */
9318 		jx9_result_int(pCtx, 0);
9319 		return JX9_OK;
9320 	}
9321 	x = jx9_value_to_double(apArg[0]);
9322 	/* Perform the requested operation */
9323 	if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
9324 		/* Base-10 log */
9325 		r = log10(x);
9326 	}else{
9327 		r = log(x);
9328 	}
9329 	/* store the result back */
9330 	jx9_result_double(pCtx, r);
9331 	return JX9_OK;
9332 }
9333 /*
9334  * float log10(float $arg )
9335  *  Base-10 logarithm.
9336  * Parameter
9337  *  The number to process.
9338  * Return
9339  *  The Base-10 logarithm of the given number.
9340  */
jx9Builtin_log10(jx9_context * pCtx,int nArg,jx9_value ** apArg)9341 static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
9342 {
9343 	double r, x;
9344 	if( nArg < 1 ){
9345 		/* Missing argument, return 0 */
9346 		jx9_result_int(pCtx, 0);
9347 		return JX9_OK;
9348 	}
9349 	x = jx9_value_to_double(apArg[0]);
9350 	/* Perform the requested operation */
9351 	r = log10(x);
9352 	/* store the result back */
9353 	jx9_result_double(pCtx, r);
9354 	return JX9_OK;
9355 }
9356 /*
9357  * number pow(number $base, number $exp)
9358  *  Exponential expression.
9359  * Parameter
9360  *  base
9361  *  The base to use.
9362  * exp
9363  *  The exponent.
9364  * Return
9365  *  base raised to the power of exp.
9366  *  If the result can be represented as integer it will be returned
9367  *  as type integer, else it will be returned as type float.
9368  */
jx9Builtin_pow(jx9_context * pCtx,int nArg,jx9_value ** apArg)9369 static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
9370 {
9371 	double r, x, y;
9372 	if( nArg < 1 ){
9373 		/* Missing argument, return 0 */
9374 		jx9_result_int(pCtx, 0);
9375 		return JX9_OK;
9376 	}
9377 	x = jx9_value_to_double(apArg[0]);
9378 	y = jx9_value_to_double(apArg[1]);
9379 	/* Perform the requested operation */
9380 	r = pow(x, y);
9381 	jx9_result_double(pCtx, r);
9382 	return JX9_OK;
9383 }
9384 /*
9385  * float pi(void)
9386  *  Returns an approximation of pi.
9387  * Note
9388  *  you can use the M_PI constant which yields identical results to pi().
9389  * Return
9390  *  The value of pi as float.
9391  */
jx9Builtin_pi(jx9_context * pCtx,int nArg,jx9_value ** apArg)9392 static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
9393 {
9394 	SXUNUSED(nArg); /* cc warning */
9395 	SXUNUSED(apArg);
9396 	jx9_result_double(pCtx, JX9_PI);
9397 	return JX9_OK;
9398 }
9399 /*
9400  * float fmod(float $x, float $y)
9401  *  Returns the floating point remainder (modulo) of the division of the arguments.
9402  * Parameters
9403  * $x
9404  *  The dividend
9405  * $y
9406  *  The divisor
9407  * Return
9408  *  The floating point remainder of x/y.
9409  */
jx9Builtin_fmod(jx9_context * pCtx,int nArg,jx9_value ** apArg)9410 static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
9411 {
9412 	double x, y, r;
9413 	if( nArg < 2 ){
9414 		/* Missing arguments */
9415 		jx9_result_double(pCtx, 0);
9416 		return JX9_OK;
9417 	}
9418 	/* Extract given arguments */
9419 	x = jx9_value_to_double(apArg[0]);
9420 	y = jx9_value_to_double(apArg[1]);
9421 	/* Perform the requested operation */
9422 	r = fmod(x, y);
9423 	/* Processing result */
9424 	jx9_result_double(pCtx, r);
9425 	return JX9_OK;
9426 }
9427 /*
9428  * float hypot(float $x, float $y)
9429  *  Calculate the length of the hypotenuse of a right-angle triangle .
9430  * Parameters
9431  * $x
9432  *  Length of first side
9433  * $y
9434  *  Length of first side
9435  * Return
9436  *  Calculated length of the hypotenuse.
9437  */
jx9Builtin_hypot(jx9_context * pCtx,int nArg,jx9_value ** apArg)9438 static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
9439 {
9440 	double x, y, r;
9441 	if( nArg < 2 ){
9442 		/* Missing arguments */
9443 		jx9_result_double(pCtx, 0);
9444 		return JX9_OK;
9445 	}
9446 	/* Extract given arguments */
9447 	x = jx9_value_to_double(apArg[0]);
9448 	y = jx9_value_to_double(apArg[1]);
9449 	/* Perform the requested operation */
9450 	r = hypot(x, y);
9451 	/* Processing result */
9452 	jx9_result_double(pCtx, r);
9453 	return JX9_OK;
9454 }
9455 #endif /* JX9_ENABLE_MATH_FUNC */
9456 /*
9457  * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
9458  *  Exponential expression.
9459  * Parameter
9460  *  $val
9461  *   The value to round.
9462  * $precision
9463  *   The optional number of decimal digits to round to.
9464  * $mode
9465  *   One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
9466  *   (not supported).
9467  * Return
9468  *  The rounded value.
9469  */
jx9Builtin_round(jx9_context * pCtx,int nArg,jx9_value ** apArg)9470 static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
9471 {
9472 	int n = 0;
9473 	double r;
9474 	if( nArg < 1 ){
9475 		/* Missing argument, return 0 */
9476 		jx9_result_int(pCtx, 0);
9477 		return JX9_OK;
9478 	}
9479 	/* Extract the precision if available */
9480 	if( nArg > 1 ){
9481 		n = jx9_value_to_int(apArg[1]);
9482 		if( n>30 ){
9483 			n = 30;
9484 		}
9485 		if( n<0 ){
9486 			n = 0;
9487 		}
9488 	}
9489 	r = jx9_value_to_double(apArg[0]);
9490 	/* If Y==0 and X will fit in a 64-bit int,
9491      * handle the rounding directly.Otherwise
9492 	 * use our own cutsom printf [i.e:SyBufferFormat()].
9493      */
9494   if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
9495     r = (double)((jx9_int64)(r+0.5));
9496   }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
9497     r = -(double)((jx9_int64)((-r)+0.5));
9498   }else{
9499 	  char zBuf[256];
9500 	  sxu32 nLen;
9501 	  nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
9502 	  /* Convert the string to real number */
9503 	  SyStrToReal(zBuf, nLen, (void *)&r, 0);
9504   }
9505   /* Return thr rounded value */
9506   jx9_result_double(pCtx, r);
9507   return JX9_OK;
9508 }
9509 /*
9510  * string dechex(int $number)
9511  *  Decimal to hexadecimal.
9512  * Parameters
9513  *  $number
9514  *   Decimal value to convert
9515  * Return
9516  *  Hexadecimal string representation of number
9517  */
jx9Builtin_dechex(jx9_context * pCtx,int nArg,jx9_value ** apArg)9518 static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
9519 {
9520 	int iVal;
9521 	if( nArg < 1 ){
9522 		/* Missing arguments, return null */
9523 		jx9_result_null(pCtx);
9524 		return JX9_OK;
9525 	}
9526 	/* Extract the given number */
9527 	iVal = jx9_value_to_int(apArg[0]);
9528 	/* Format */
9529 	jx9_result_string_format(pCtx, "%x", iVal);
9530 	return JX9_OK;
9531 }
9532 /*
9533  * string decoct(int $number)
9534  *  Decimal to Octal.
9535  * Parameters
9536  *  $number
9537  *   Decimal value to convert
9538  * Return
9539  *  Octal string representation of number
9540  */
jx9Builtin_decoct(jx9_context * pCtx,int nArg,jx9_value ** apArg)9541 static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
9542 {
9543 	int iVal;
9544 	if( nArg < 1 ){
9545 		/* Missing arguments, return null */
9546 		jx9_result_null(pCtx);
9547 		return JX9_OK;
9548 	}
9549 	/* Extract the given number */
9550 	iVal = jx9_value_to_int(apArg[0]);
9551 	/* Format */
9552 	jx9_result_string_format(pCtx, "%o", iVal);
9553 	return JX9_OK;
9554 }
9555 /*
9556  * string decbin(int $number)
9557  *  Decimal to binary.
9558  * Parameters
9559  *  $number
9560  *   Decimal value to convert
9561  * Return
9562  *  Binary string representation of number
9563  */
jx9Builtin_decbin(jx9_context * pCtx,int nArg,jx9_value ** apArg)9564 static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
9565 {
9566 	int iVal;
9567 	if( nArg < 1 ){
9568 		/* Missing arguments, return null */
9569 		jx9_result_null(pCtx);
9570 		return JX9_OK;
9571 	}
9572 	/* Extract the given number */
9573 	iVal = jx9_value_to_int(apArg[0]);
9574 	/* Format */
9575 	jx9_result_string_format(pCtx, "%B", iVal);
9576 	return JX9_OK;
9577 }
9578 /*
9579  * int64 hexdec(string $hex_string)
9580  *  Hexadecimal to decimal.
9581  * Parameters
9582  *  $hex_string
9583  *   The hexadecimal string to convert
9584  * Return
9585  *  The decimal representation of hex_string
9586  */
jx9Builtin_hexdec(jx9_context * pCtx,int nArg,jx9_value ** apArg)9587 static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9588 {
9589 	const char *zString, *zEnd;
9590 	jx9_int64 iVal;
9591 	int nLen;
9592 	if( nArg < 1 ){
9593 		/* Missing arguments, return -1 */
9594 		jx9_result_int(pCtx, -1);
9595 		return JX9_OK;
9596 	}
9597 	iVal = 0;
9598 	if( jx9_value_is_string(apArg[0]) ){
9599 		/* Extract the given string */
9600 		zString = jx9_value_to_string(apArg[0], &nLen);
9601 		/* Delimit the string */
9602 		zEnd = &zString[nLen];
9603 		/* Ignore non hex-stream */
9604 		while( zString < zEnd ){
9605 			if( (unsigned char)zString[0] >= 0xc0 ){
9606 				/* UTF-8 stream */
9607 				zString++;
9608 				while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
9609 					zString++;
9610 				}
9611 			}else{
9612 				if( SyisHex(zString[0]) ){
9613 					break;
9614 				}
9615 				/* Ignore */
9616 				zString++;
9617 			}
9618 		}
9619 		if( zString < zEnd ){
9620 			/* Cast */
9621 			SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
9622 		}
9623 	}else{
9624 		/* Extract as a 64-bit integer */
9625 		iVal = jx9_value_to_int64(apArg[0]);
9626 	}
9627 	/* Return the number */
9628 	jx9_result_int64(pCtx, iVal);
9629 	return JX9_OK;
9630 }
9631 /*
9632  * int64 bindec(string $bin_string)
9633  *  Binary to decimal.
9634  * Parameters
9635  *  $bin_string
9636  *   The binary string to convert
9637  * Return
9638  *  Returns the decimal equivalent of the binary number represented by the binary_string argument.
9639  */
jx9Builtin_bindec(jx9_context * pCtx,int nArg,jx9_value ** apArg)9640 static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9641 {
9642 	const char *zString;
9643 	jx9_int64 iVal;
9644 	int nLen;
9645 	if( nArg < 1 ){
9646 		/* Missing arguments, return -1 */
9647 		jx9_result_int(pCtx, -1);
9648 		return JX9_OK;
9649 	}
9650 	iVal = 0;
9651 	if( jx9_value_is_string(apArg[0]) ){
9652 		/* Extract the given string */
9653 		zString = jx9_value_to_string(apArg[0], &nLen);
9654 		if( nLen > 0 ){
9655 			/* Perform a binary cast */
9656 			SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
9657 		}
9658 	}else{
9659 		/* Extract as a 64-bit integer */
9660 		iVal = jx9_value_to_int64(apArg[0]);
9661 	}
9662 	/* Return the number */
9663 	jx9_result_int64(pCtx, iVal);
9664 	return JX9_OK;
9665 }
9666 /*
9667  * int64 octdec(string $oct_string)
9668  *  Octal to decimal.
9669  * Parameters
9670  *  $oct_string
9671  *   The octal string to convert
9672  * Return
9673  *  Returns the decimal equivalent of the octal number represented by the octal_string argument.
9674  */
jx9Builtin_octdec(jx9_context * pCtx,int nArg,jx9_value ** apArg)9675 static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
9676 {
9677 	const char *zString;
9678 	jx9_int64 iVal;
9679 	int nLen;
9680 	if( nArg < 1 ){
9681 		/* Missing arguments, return -1 */
9682 		jx9_result_int(pCtx, -1);
9683 		return JX9_OK;
9684 	}
9685 	iVal = 0;
9686 	if( jx9_value_is_string(apArg[0]) ){
9687 		/* Extract the given string */
9688 		zString = jx9_value_to_string(apArg[0], &nLen);
9689 		if( nLen > 0 ){
9690 			/* Perform the cast */
9691 			SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
9692 		}
9693 	}else{
9694 		/* Extract as a 64-bit integer */
9695 		iVal = jx9_value_to_int64(apArg[0]);
9696 	}
9697 	/* Return the number */
9698 	jx9_result_int64(pCtx, iVal);
9699 	return JX9_OK;
9700 }
9701 /*
9702  * string base_convert(string $number, int $frombase, int $tobase)
9703  *  Convert a number between arbitrary bases.
9704  * Parameters
9705  * $number
9706  *  The number to convert
9707  * $frombase
9708  *  The base number is in
9709  * $tobase
9710  *  The base to convert number to
9711  * Return
9712  *  Number converted to base tobase
9713  */
jx9Builtin_base_convert(jx9_context * pCtx,int nArg,jx9_value ** apArg)9714 static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
9715 {
9716 	int nLen, iFbase, iTobase;
9717 	const char *zNum;
9718 	jx9_int64 iNum;
9719 	if( nArg < 3 ){
9720 		/* Return the empty string*/
9721 		jx9_result_string(pCtx, "", 0);
9722 		return JX9_OK;
9723 	}
9724 	/* Base numbers */
9725 	iFbase  = jx9_value_to_int(apArg[1]);
9726 	iTobase = jx9_value_to_int(apArg[2]);
9727 	if( jx9_value_is_string(apArg[0]) ){
9728 		/* Extract the target number */
9729 		zNum = jx9_value_to_string(apArg[0], &nLen);
9730 		if( nLen < 1 ){
9731 			/* Return the empty string*/
9732 			jx9_result_string(pCtx, "", 0);
9733 			return JX9_OK;
9734 		}
9735 		/* Base conversion */
9736 		switch(iFbase){
9737 		case 16:
9738 			/* Hex */
9739 			SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9740 			break;
9741 		case 8:
9742 			/* Octal */
9743 			SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9744 			break;
9745 		case 2:
9746 			/* Binary */
9747 			SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9748 			break;
9749 		default:
9750 			/* Decimal */
9751 			SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
9752 			break;
9753 		}
9754 	}else{
9755 		iNum = jx9_value_to_int64(apArg[0]);
9756 	}
9757 	switch(iTobase){
9758 	case 16:
9759 		/* Hex */
9760 		jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
9761 		break;
9762 	case 8:
9763 		/* Octal */
9764 		jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
9765 		break;
9766 	case 2:
9767 		/* Binary */
9768 		jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
9769 		break;
9770 	default:
9771 		/* Decimal */
9772 		jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
9773 		break;
9774 	}
9775 	return JX9_OK;
9776 }
9777 /*
9778  * Section:
9779  *    String handling Functions.
9780  * Authors:
9781  *    Symisc Systems, devel@symisc.net.
9782  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
9783  * Status:
9784  *    Stable.
9785  */
9786 /*
9787  * string substr(string $string, int $start[, int $length ])
9788  *  Return part of a string.
9789  * Parameters
9790  *  $string
9791  *   The input string. Must be one character or longer.
9792  * $start
9793  *   If start is non-negative, the returned string will start at the start'th position
9794  *   in string, counting from zero. For instance, in the string 'abcdef', the character
9795  *   at position 0 is 'a', the character at position 2 is 'c', and so forth.
9796  *   If start is negative, the returned string will start at the start'th character
9797  *   from the end of string.
9798  *   If string is less than or equal to start characters long, FALSE will be returned.
9799  * $length
9800  *   If length is given and is positive, the string returned will contain at most length
9801  *   characters beginning from start (depending on the length of string).
9802  *   If length is given and is negative, then that many characters will be omitted from
9803  *   the end of string (after the start position has been calculated when a start is negative).
9804  *   If start denotes the position of this truncation or beyond, false will be returned.
9805  *   If length is given and is 0, FALSE or NULL an empty string will be returned.
9806  *   If length is omitted, the substring starting from start until the end of the string
9807  *   will be returned.
9808  * Return
9809  *  Returns the extracted part of string, or FALSE on failure or an empty string.
9810  */
jx9Builtin_substr(jx9_context * pCtx,int nArg,jx9_value ** apArg)9811 static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
9812 {
9813 	const char *zSource, *zOfft;
9814 	int nOfft, nLen, nSrcLen;
9815 	if( nArg < 2 ){
9816 		/* return FALSE */
9817 		jx9_result_bool(pCtx, 0);
9818 		return JX9_OK;
9819 	}
9820 	/* Extract the target string */
9821 	zSource = jx9_value_to_string(apArg[0], &nSrcLen);
9822 	if( nSrcLen < 1 ){
9823 		/* Empty string, return FALSE */
9824 		jx9_result_bool(pCtx, 0);
9825 		return JX9_OK;
9826 	}
9827 	nLen = nSrcLen; /* cc warning */
9828 	/* Extract the offset */
9829 	nOfft = jx9_value_to_int(apArg[1]);
9830 	if( nOfft < 0 ){
9831 		zOfft = &zSource[nSrcLen+nOfft];
9832 		if( zOfft < zSource ){
9833 			/* Invalid offset */
9834 			jx9_result_bool(pCtx, 0);
9835 			return JX9_OK;
9836 		}
9837 		nLen = (int)(&zSource[nSrcLen]-zOfft);
9838 		nOfft = (int)(zOfft-zSource);
9839 	}else if( nOfft >= nSrcLen ){
9840 		/* Invalid offset */
9841 		jx9_result_bool(pCtx, 0);
9842 		return JX9_OK;
9843 	}else{
9844 		zOfft = &zSource[nOfft];
9845 		nLen = nSrcLen - nOfft;
9846 	}
9847 	if( nArg > 2 ){
9848 		/* Extract the length */
9849 		nLen = jx9_value_to_int(apArg[2]);
9850 		if( nLen == 0 ){
9851 			/* Invalid length, return an empty string */
9852 			jx9_result_string(pCtx, "", 0);
9853 			return JX9_OK;
9854 		}else if( nLen < 0 ){
9855 			nLen = nSrcLen + nLen - nOfft;
9856 			if( nLen < 1 ){
9857 				/* Invalid  length */
9858 				nLen = nSrcLen - nOfft;
9859 			}
9860 		}
9861 		if( nLen + nOfft > nSrcLen ){
9862 			/* Invalid length */
9863 			nLen = nSrcLen - nOfft;
9864 		}
9865 	}
9866 	/* Return the substring */
9867 	jx9_result_string(pCtx, zOfft, nLen);
9868 	return JX9_OK;
9869 }
9870 /*
9871  * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
9872  *  Binary safe comparison of two strings from an offset, up to length characters.
9873  * Parameters
9874  *  $main_str
9875  *  The main string being compared.
9876  *  $str
9877  *   The secondary string being compared.
9878  * $offset
9879  *  The start position for the comparison. If negative, it starts counting from
9880  *  the end of the string.
9881  * $length
9882  *  The length of the comparison. The default value is the largest of the length
9883  *  of the str compared to the length of main_str less the offset.
9884  * $case_insensitivity
9885  *  If case_insensitivity is TRUE, comparison is case insensitive.
9886  * Return
9887  *  Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
9888  *  str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
9889  *  or length is set and is less than 1, substr_compare() prints a warning and returns FALSE.
9890  */
jx9Builtin_substr_compare(jx9_context * pCtx,int nArg,jx9_value ** apArg)9891 static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
9892 {
9893 	const char *zSource, *zOfft, *zSub;
9894 	int nOfft, nLen, nSrcLen, nSublen;
9895 	int iCase = 0;
9896 	int rc;
9897 	if( nArg < 3 ){
9898 		/* Missing arguments, return FALSE */
9899 		jx9_result_bool(pCtx, 0);
9900 		return JX9_OK;
9901 	}
9902 	/* Extract the target string */
9903 	zSource = jx9_value_to_string(apArg[0], &nSrcLen);
9904 	if( nSrcLen < 1 ){
9905 		/* Empty string, return FALSE */
9906 		jx9_result_bool(pCtx, 0);
9907 		return JX9_OK;
9908 	}
9909 	nLen = nSrcLen; /* cc warning */
9910 	/* Extract the substring */
9911 	zSub = jx9_value_to_string(apArg[1], &nSublen);
9912 	if( nSublen < 1 || nSublen > nSrcLen){
9913 		/* Empty string, return FALSE */
9914 		jx9_result_bool(pCtx, 0);
9915 		return JX9_OK;
9916 	}
9917 	/* Extract the offset */
9918 	nOfft = jx9_value_to_int(apArg[2]);
9919 	if( nOfft < 0 ){
9920 		zOfft = &zSource[nSrcLen+nOfft];
9921 		if( zOfft < zSource ){
9922 			/* Invalid offset */
9923 			jx9_result_bool(pCtx, 0);
9924 			return JX9_OK;
9925 		}
9926 		nLen = (int)(&zSource[nSrcLen]-zOfft);
9927 		nOfft = (int)(zOfft-zSource);
9928 	}else if( nOfft >= nSrcLen ){
9929 		/* Invalid offset */
9930 		jx9_result_bool(pCtx, 0);
9931 		return JX9_OK;
9932 	}else{
9933 		zOfft = &zSource[nOfft];
9934 		nLen = nSrcLen - nOfft;
9935 	}
9936 	if( nArg > 3 ){
9937 		/* Extract the length */
9938 		nLen = jx9_value_to_int(apArg[3]);
9939 		if( nLen < 1 ){
9940 			/* Invalid  length */
9941 			jx9_result_int(pCtx, 1);
9942 			return JX9_OK;
9943 		}else if( nLen + nOfft > nSrcLen ){
9944 			/* Invalid length */
9945 			nLen = nSrcLen - nOfft;
9946 		}
9947 		if( nArg > 4 ){
9948 			/* Case-sensitive or not */
9949 			iCase = jx9_value_to_bool(apArg[4]);
9950 		}
9951 	}
9952 	/* Perform the comparison */
9953 	if( iCase ){
9954 		rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
9955 	}else{
9956 		rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
9957 	}
9958 	/* Comparison result */
9959 	jx9_result_int(pCtx, rc);
9960 	return JX9_OK;
9961 }
9962 /*
9963  * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
9964  *  Count the number of substring occurrences.
9965  * Parameters
9966  * $haystack
9967  *   The string to search in
9968  * $needle
9969  *   The substring to search for
9970  * $offset
9971  *  The offset where to start counting
9972  * $length (NOT USED)
9973  *  The maximum length after the specified offset to search for the substring.
9974  *  It outputs a warning if the offset plus the length is greater than the haystack length.
9975  * Return
9976  *  Toral number of substring occurrences.
9977  */
jx9Builtin_substr_count(jx9_context * pCtx,int nArg,jx9_value ** apArg)9978 static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
9979 {
9980 	const char *zText, *zPattern, *zEnd;
9981 	int nTextlen, nPatlen;
9982 	int iCount = 0;
9983 	sxu32 nOfft;
9984 	sxi32 rc;
9985 	if( nArg < 2 ){
9986 		/* Missing arguments */
9987 		jx9_result_int(pCtx, 0);
9988 		return JX9_OK;
9989 	}
9990 	/* Point to the haystack */
9991 	zText = jx9_value_to_string(apArg[0], &nTextlen);
9992 	/* Point to the neddle */
9993 	zPattern = jx9_value_to_string(apArg[1], &nPatlen);
9994 	if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
9995 		/* NOOP, return zero */
9996 		jx9_result_int(pCtx, 0);
9997 		return JX9_OK;
9998 	}
9999 	if( nArg > 2 ){
10000 		int nOfft;
10001 		/* Extract the offset */
10002 		nOfft = jx9_value_to_int(apArg[2]);
10003 		if( nOfft < 0 || nOfft > nTextlen ){
10004 			/* Invalid offset, return zero */
10005 			jx9_result_int(pCtx, 0);
10006 			return JX9_OK;
10007 		}
10008 		/* Point to the desired offset */
10009 		zText = &zText[nOfft];
10010 		/* Adjust length */
10011 		nTextlen -= nOfft;
10012 	}
10013 	/* Point to the end of the string */
10014 	zEnd = &zText[nTextlen];
10015 	if( nArg > 3 ){
10016 		int nLen;
10017 		/* Extract the length */
10018 		nLen = jx9_value_to_int(apArg[3]);
10019 		if( nLen < 0 || nLen > nTextlen ){
10020 			/* Invalid length, return 0 */
10021 			jx9_result_int(pCtx, 0);
10022 			return JX9_OK;
10023 		}
10024 		/* Adjust pointer */
10025 		nTextlen = nLen;
10026 		zEnd = &zText[nTextlen];
10027 	}
10028 	/* Perform the search */
10029 	for(;;){
10030 		rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
10031 		if( rc != SXRET_OK ){
10032 			/* Pattern not found, break immediately */
10033 			break;
10034 		}
10035 		/* Increment counter and update the offset */
10036 		iCount++;
10037 		zText += nOfft + nPatlen;
10038 		if( zText >= zEnd ){
10039 			break;
10040 		}
10041 	}
10042 	/* Pattern count */
10043 	jx9_result_int(pCtx, iCount);
10044 	return JX9_OK;
10045 }
10046 /*
10047  * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
10048  *   Split a string into smaller chunks.
10049  * Parameters
10050  *  $body
10051  *   The string to be chunked.
10052  * $chunklen
10053  *   The chunk length.
10054  * $end
10055  *   The line ending sequence.
10056  * Return
10057  *  The chunked string or NULL on failure.
10058  */
jx9Builtin_chunk_split(jx9_context * pCtx,int nArg,jx9_value ** apArg)10059 static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
10060 {
10061 	const char *zIn, *zEnd, *zSep = "\r\n";
10062 	int nSepLen, nChunkLen, nLen;
10063 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10064 		/* Nothing to split, return null */
10065 		jx9_result_null(pCtx);
10066 		return JX9_OK;
10067 	}
10068 	/* initialize/Extract arguments */
10069 	nSepLen = (int)sizeof("\r\n") - 1;
10070 	nChunkLen = 76;
10071 	zIn = jx9_value_to_string(apArg[0], &nLen);
10072 	zEnd = &zIn[nLen];
10073 	if( nArg > 1 ){
10074 		/* Chunk length */
10075 		nChunkLen = jx9_value_to_int(apArg[1]);
10076 		if( nChunkLen < 1 ){
10077 			/* Switch back to the default length */
10078 			nChunkLen = 76;
10079 		}
10080 		if( nArg > 2 ){
10081 			/* Separator */
10082 			zSep = jx9_value_to_string(apArg[2], &nSepLen);
10083 			if( nSepLen < 1 ){
10084 				/* Switch back to the default separator */
10085 				zSep = "\r\n";
10086 				nSepLen = (int)sizeof("\r\n") - 1;
10087 			}
10088 		}
10089 	}
10090 	/* Perform the requested operation */
10091 	if( nChunkLen > nLen ){
10092 		/* Nothing to split, return the string and the separator */
10093 		jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
10094 		return JX9_OK;
10095 	}
10096 	while( zIn < zEnd ){
10097 		if( nChunkLen > (int)(zEnd-zIn) ){
10098 			nChunkLen = (int)(zEnd - zIn);
10099 		}
10100 		/* Append the chunk and the separator */
10101 		jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
10102 		/* Point beyond the chunk */
10103 		zIn += nChunkLen;
10104 	}
10105 	return JX9_OK;
10106 }
10107 /*
10108  * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
10109  *  HTML escaping of special characters.
10110  *  The translations performed are:
10111  *   '&' (ampersand) ==> '&amp;'
10112  *   '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
10113  *   "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
10114  *   '<' (less than) ==> '&lt;'
10115  *   '>' (greater than) ==> '&gt;'
10116  * Parameters
10117  *  $string
10118  *   The string being converted.
10119  * $flags
10120  *   A bitmask of one or more of the following flags, which specify how to handle quotes.
10121  *   The default is ENT_COMPAT | ENT_HTML401.
10122  *   ENT_COMPAT 	Will convert double-quotes and leave single-quotes alone.
10123  *   ENT_QUOTES 	Will convert both double and single quotes.
10124  *   ENT_NOQUOTES 	Will leave both double and single quotes unconverted.
10125  *   ENT_IGNORE 	Silently discard invalid code unit sequences instead of returning an empty string.
10126  * $charset
10127  *  Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
10128  * Return
10129  *  The escaped string or NULL on failure.
10130  */
jx9Builtin_htmlspecialchars(jx9_context * pCtx,int nArg,jx9_value ** apArg)10131 static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
10132 {
10133 	const char *zCur, *zIn, *zEnd;
10134 	int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
10135 	int nLen, c;
10136 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10137 		/* Missing/Invalid arguments, return NULL */
10138 		jx9_result_null(pCtx);
10139 		return JX9_OK;
10140 	}
10141 	/* Extract the target string */
10142 	zIn = jx9_value_to_string(apArg[0], &nLen);
10143 	zEnd = &zIn[nLen];
10144 	/* Extract the flags if available */
10145 	if( nArg > 1 ){
10146 		iFlags = jx9_value_to_int(apArg[1]);
10147 		if( iFlags < 0 ){
10148 			iFlags = 0x01|0x40;
10149 		}
10150 	}
10151 	/* Perform the requested operation */
10152 	for(;;){
10153 		if( zIn >= zEnd ){
10154 			break;
10155 		}
10156 		zCur = zIn;
10157 		while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
10158 			zIn++;
10159 		}
10160 		if( zCur < zIn ){
10161 			/* Append the raw string verbatim */
10162 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10163 		}
10164 		if( zIn >= zEnd ){
10165 			break;
10166 		}
10167 		c = zIn[0];
10168 		if( c == '&' ){
10169 			/* Expand '&amp;' */
10170 			jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
10171 		}else if( c == '<' ){
10172 			/* Expand '&lt;' */
10173 			jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
10174 		}else if( c == '>' ){
10175 			/* Expand '&gt;' */
10176 			jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
10177 		}else if( c == '\'' ){
10178 			if( iFlags & 0x02 /*ENT_QUOTES*/ ){
10179 				/* Expand '&#039;' */
10180 				jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
10181 			}else{
10182 				/* Leave the single quote untouched */
10183 				jx9_result_string(pCtx, "'", (int)sizeof(char));
10184 			}
10185 		}else if( c == '"' ){
10186 			if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
10187 				/* Expand '&quot;' */
10188 				jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
10189 			}else{
10190 				/* Leave the double quote untouched */
10191 				jx9_result_string(pCtx, "\"", (int)sizeof(char));
10192 			}
10193 		}
10194 		/* Ignore the unsafe HTML character */
10195 		zIn++;
10196 	}
10197 	return JX9_OK;
10198 }
10199 /*
10200  * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
10201  *  Unescape HTML entities.
10202  * Parameters
10203  *  $string
10204  *   The string to decode
10205  *  $quote_style
10206  *    The quote style. One of the following constants:
10207  *   ENT_COMPAT 	Will convert double-quotes and leave single-quotes alone (default)
10208  *   ENT_QUOTES 	Will convert both double and single quotes
10209  *   ENT_NOQUOTES 	Will leave both double and single quotes unconverted
10210  * Return
10211  *  The unescaped string or NULL on failure.
10212  */
jx9Builtin_htmlspecialchars_decode(jx9_context * pCtx,int nArg,jx9_value ** apArg)10213 static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10214 {
10215 	const char *zCur, *zIn, *zEnd;
10216 	int iFlags = 0x01; /* ENT_COMPAT */
10217 	int nLen, nJump;
10218 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10219 		/* Missing/Invalid arguments, return NULL */
10220 		jx9_result_null(pCtx);
10221 		return JX9_OK;
10222 	}
10223 	/* Extract the target string */
10224 	zIn = jx9_value_to_string(apArg[0], &nLen);
10225 	zEnd = &zIn[nLen];
10226 	/* Extract the flags if available */
10227 	if( nArg > 1 ){
10228 		iFlags = jx9_value_to_int(apArg[1]);
10229 		if( iFlags < 0 ){
10230 			iFlags = 0x01;
10231 		}
10232 	}
10233 	/* Perform the requested operation */
10234 	for(;;){
10235 		if( zIn >= zEnd ){
10236 			break;
10237 		}
10238 		zCur = zIn;
10239 		while( zIn < zEnd && zIn[0] != '&' ){
10240 			zIn++;
10241 		}
10242 		if( zCur < zIn ){
10243 			/* Append the raw string verbatim */
10244 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10245 		}
10246 		nLen = (int)(zEnd-zIn);
10247 		nJump = (int)sizeof(char);
10248 		if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
10249 			/* &amp; ==> '&' */
10250 			jx9_result_string(pCtx, "&", (int)sizeof(char));
10251 			nJump = (int)sizeof("&amp;")-1;
10252 		}else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
10253 			/* &lt; ==> < */
10254 			jx9_result_string(pCtx, "<", (int)sizeof(char));
10255 			nJump = (int)sizeof("&lt;")-1;
10256 		}else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
10257 			/* &gt; ==> '>' */
10258 			jx9_result_string(pCtx, ">", (int)sizeof(char));
10259 			nJump = (int)sizeof("&gt;")-1;
10260 		}else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
10261 			/* &quot; ==> '"' */
10262 			if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
10263 				jx9_result_string(pCtx, "\"", (int)sizeof(char));
10264 			}else{
10265 				/* Leave untouched */
10266 				jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
10267 			}
10268 			nJump = (int)sizeof("&quot;")-1;
10269 		}else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
10270 			/* &#039; ==> ''' */
10271 			if( iFlags & 0x02 /*ENT_QUOTES*/ ){
10272 				/* Expand ''' */
10273 				jx9_result_string(pCtx, "'", (int)sizeof(char));
10274 			}else{
10275 				/* Leave untouched */
10276 				jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
10277 			}
10278 			nJump = (int)sizeof("&#039;")-1;
10279 		}else if( nLen >= (int)sizeof(char) ){
10280 			/* expand '&' */
10281 			jx9_result_string(pCtx, "&", (int)sizeof(char));
10282 		}else{
10283 			/* No more input to process */
10284 			break;
10285 		}
10286 		zIn += nJump;
10287 	}
10288 	return JX9_OK;
10289 }
10290 /* HTML encoding/Decoding table
10291  * Source: Symisc RunTime API.[chm@symisc.net]
10292  */
10293 static const char *azHtmlEscape[] = {
10294  	"&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'",
10295 	"&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(",
10296 	"&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+",
10297 	"&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", ","
10298  };
10299 /*
10300  * array get_html_translation_table(void)
10301  *  Returns the translation table used by htmlspecialchars() and htmlentities().
10302  * Parameters
10303  *  None
10304  * Return
10305  *  The translation table as an array or NULL on failure.
10306  */
jx9Builtin_get_html_translation_table(jx9_context * pCtx,int nArg,jx9_value ** apArg)10307 static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
10308 {
10309 	jx9_value *pArray, *pValue;
10310 	sxu32 n;
10311 	/* Element value */
10312 	pValue = jx9_context_new_scalar(pCtx);
10313 	if( pValue == 0 ){
10314 		SXUNUSED(nArg); /* cc warning */
10315 		SXUNUSED(apArg);
10316 		/* Return NULL */
10317 		jx9_result_null(pCtx);
10318 		return JX9_OK;
10319 	}
10320 	/* Create a new array */
10321 	pArray = jx9_context_new_array(pCtx);
10322 	if( pArray == 0 ){
10323 		/* Return NULL */
10324 		jx9_result_null(pCtx);
10325 		return JX9_OK;
10326 	}
10327 	/* Make the table */
10328 	for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10329 		/* Prepare the value */
10330 		jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
10331 		/* Insert the value */
10332 		jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
10333 		/* Reset the string cursor */
10334 		jx9_value_reset_string_cursor(pValue);
10335 	}
10336 	/*
10337 	 * Return the array.
10338 	 * Don't worry about freeing memory, everything will be automatically
10339 	 * released upon we return from this function.
10340 	 */
10341 	jx9_result_value(pCtx, pArray);
10342 	return JX9_OK;
10343 }
10344 /*
10345  * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
10346  *   Convert all applicable characters to HTML entities
10347  * Parameters
10348  * $string
10349  *   The input string.
10350  * $flags
10351  *  A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
10352  * Return
10353  * The encoded string.
10354  */
jx9Builtin_htmlentities(jx9_context * pCtx,int nArg,jx9_value ** apArg)10355 static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
10356 {
10357 	int iFlags = 0x01; /* ENT_COMPAT */
10358 	const char *zIn, *zEnd;
10359 	int nLen, c;
10360 	sxu32 n;
10361 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10362 		/* Missing/Invalid arguments, return NULL */
10363 		jx9_result_null(pCtx);
10364 		return JX9_OK;
10365 	}
10366 	/* Extract the target string */
10367 	zIn = jx9_value_to_string(apArg[0], &nLen);
10368 	zEnd = &zIn[nLen];
10369 	/* Extract the flags if available */
10370 	if( nArg > 1 ){
10371 		iFlags = jx9_value_to_int(apArg[1]);
10372 		if( iFlags < 0 ){
10373 			iFlags = 0x01;
10374 		}
10375 	}
10376 	/* Perform the requested operation */
10377 	for(;;){
10378 		if( zIn >= zEnd ){
10379 			/* No more input to process */
10380 			break;
10381 		}
10382 		c = zIn[0];
10383 		/* Perform a linear lookup on the decoding table */
10384 		for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10385 			if( azHtmlEscape[n+1][0] == c ){
10386 				/* Got one */
10387 				break;
10388 			}
10389 		}
10390 		if( n < SX_ARRAYSIZE(azHtmlEscape) ){
10391 			/* Output the safe sequence [i.e: '<' ==> '&lt;"] */
10392 			if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
10393 				/* Expand the double quote verbatim */
10394 				jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10395 			}else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
10396 				/* expand single quote verbatim */
10397 				jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10398 			}else{
10399 				jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
10400 			}
10401 		}else{
10402 			/* Output character verbatim */
10403 			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
10404 		}
10405 		zIn++;
10406 	}
10407 	return JX9_OK;
10408 }
10409 /*
10410  * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
10411  *   Perform the reverse operation of html_entity_decode().
10412  * Parameters
10413  * $string
10414  *   The input string.
10415  * $flags
10416  *  A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
10417  * Return
10418  * The decoded string.
10419  */
jx9Builtin_html_entity_decode(jx9_context * pCtx,int nArg,jx9_value ** apArg)10420 static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10421 {
10422 	const char *zCur, *zIn, *zEnd;
10423 	int iFlags = 0x01; /* ENT_COMPAT  */
10424 	int nLen;
10425 	sxu32 n;
10426 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
10427 		/* Missing/Invalid arguments, return NULL */
10428 		jx9_result_null(pCtx);
10429 		return JX9_OK;
10430 	}
10431 	/* Extract the target string */
10432 	zIn = jx9_value_to_string(apArg[0], &nLen);
10433 	zEnd = &zIn[nLen];
10434 	/* Extract the flags if available */
10435 	if( nArg > 1 ){
10436 		iFlags = jx9_value_to_int(apArg[1]);
10437 		if( iFlags < 0 ){
10438 			iFlags = 0x01;
10439 		}
10440 	}
10441 	/* Perform the requested operation */
10442 	for(;;){
10443 		if( zIn >= zEnd ){
10444 			/* No more input to process */
10445 			break;
10446 		}
10447 		zCur = zIn;
10448 		while( zIn < zEnd && zIn[0] != '&' ){
10449 			zIn++;
10450 		}
10451 		if( zCur < zIn ){
10452 			/* Append raw string verbatim */
10453 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
10454 		}
10455 		if( zIn >= zEnd ){
10456 			break;
10457 		}
10458 		nLen = (int)(zEnd-zIn);
10459 		/* Find an encoded sequence */
10460 		for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
10461 			int iLen = (int)SyStrlen(azHtmlEscape[n]);
10462 			if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
10463 				/* Got one */
10464 				zIn += iLen;
10465 				break;
10466 			}
10467 		}
10468 		if( n < SX_ARRAYSIZE(azHtmlEscape) ){
10469 			int c = azHtmlEscape[n+1][0];
10470 			/* Output the decoded character */
10471 			if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/)  ){
10472 				/* Do not process single quotes */
10473 				jx9_result_string(pCtx, azHtmlEscape[n], -1);
10474 			}else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
10475 				/* Do not process double quotes */
10476 				jx9_result_string(pCtx, azHtmlEscape[n], -1);
10477 			}else{
10478 				jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
10479 			}
10480 		}else{
10481 			/* Append '&' */
10482 			jx9_result_string(pCtx, "&", (int)sizeof(char));
10483 			zIn++;
10484 		}
10485 	}
10486 	return JX9_OK;
10487 }
10488 /*
10489  * int strlen($string)
10490  *  return the length of the given string.
10491  * Parameter
10492  *  string: The string being measured for length.
10493  * Return
10494  *  length of the given string.
10495  */
jx9Builtin_strlen(jx9_context * pCtx,int nArg,jx9_value ** apArg)10496 static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
10497 {
10498 	int iLen = 0;
10499 	if( nArg > 0 ){
10500 		jx9_value_to_string(apArg[0], &iLen);
10501 	}
10502 	/* String length */
10503 	jx9_result_int(pCtx, iLen);
10504 	return JX9_OK;
10505 }
10506 /*
10507  * int strcmp(string $str1, string $str2)
10508  *  Perform a binary safe string comparison.
10509  * Parameter
10510  *  str1: The first string
10511  *  str2: The second string
10512  * Return
10513  *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10514  *  than str2, and 0 if they are equal.
10515  */
jx9Builtin_strcmp(jx9_context * pCtx,int nArg,jx9_value ** apArg)10516 static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10517 {
10518 	const char *z1, *z2;
10519 	int n1, n2;
10520 	int res;
10521 	if( nArg < 2 ){
10522 		res = nArg == 0 ? 0 : 1;
10523 		jx9_result_int(pCtx, res);
10524 		return JX9_OK;
10525 	}
10526 	/* Perform the comparison */
10527 	z1 = jx9_value_to_string(apArg[0], &n1);
10528 	z2 = jx9_value_to_string(apArg[1], &n2);
10529 	res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
10530 	/* Comparison result */
10531 	jx9_result_int(pCtx, res);
10532 	return JX9_OK;
10533 }
10534 /*
10535  * int strncmp(string $str1, string $str2, int n)
10536  *  Perform a binary safe string comparison of the first n characters.
10537  * Parameter
10538  *  str1: The first string
10539  *  str2: The second string
10540  * Return
10541  *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10542  *  than str2, and 0 if they are equal.
10543  */
jx9Builtin_strncmp(jx9_context * pCtx,int nArg,jx9_value ** apArg)10544 static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10545 {
10546 	const char *z1, *z2;
10547 	int res;
10548 	int n;
10549 	if( nArg < 3 ){
10550 		/* Perform a standard comparison */
10551 		return jx9Builtin_strcmp(pCtx, nArg, apArg);
10552 	}
10553 	/* Desired comparison length */
10554 	n  = jx9_value_to_int(apArg[2]);
10555 	if( n < 0 ){
10556 		/* Invalid length */
10557 		jx9_result_int(pCtx, -1);
10558 		return JX9_OK;
10559 	}
10560 	/* Perform the comparison */
10561 	z1 = jx9_value_to_string(apArg[0], 0);
10562 	z2 = jx9_value_to_string(apArg[1], 0);
10563 	res = SyStrncmp(z1, z2, (sxu32)n);
10564 	/* Comparison result */
10565 	jx9_result_int(pCtx, res);
10566 	return JX9_OK;
10567 }
10568 /*
10569  * int strcasecmp(string $str1, string $str2, int n)
10570  *  Perform a binary safe case-insensitive string comparison.
10571  * Parameter
10572  *  str1: The first string
10573  *  str2: The second string
10574  * Return
10575  *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10576  *  than str2, and 0 if they are equal.
10577  */
jx9Builtin_strcasecmp(jx9_context * pCtx,int nArg,jx9_value ** apArg)10578 static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10579 {
10580 	const char *z1, *z2;
10581 	int n1, n2;
10582 	int res;
10583 	if( nArg < 2 ){
10584 		res = nArg == 0 ? 0 : 1;
10585 		jx9_result_int(pCtx, res);
10586 		return JX9_OK;
10587 	}
10588 	/* Perform the comparison */
10589 	z1 = jx9_value_to_string(apArg[0], &n1);
10590 	z2 = jx9_value_to_string(apArg[1], &n2);
10591 	res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
10592 	/* Comparison result */
10593 	jx9_result_int(pCtx, res);
10594 	return JX9_OK;
10595 }
10596 /*
10597  * int strncasecmp(string $str1, string $str2, int n)
10598  *  Perform a binary safe case-insensitive string comparison of the first n characters.
10599  * Parameter
10600  *  $str1: The first string
10601  *  $str2: The second string
10602  *  $len:  The length of strings to be used in the comparison.
10603  * Return
10604  *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater
10605  *  than str2, and 0 if they are equal.
10606  */
jx9Builtin_strncasecmp(jx9_context * pCtx,int nArg,jx9_value ** apArg)10607 static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
10608 {
10609 	const char *z1, *z2;
10610 	int res;
10611 	int n;
10612 	if( nArg < 3 ){
10613 		/* Perform a standard comparison */
10614 		return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
10615 	}
10616 	/* Desired comparison length */
10617 	n  = jx9_value_to_int(apArg[2]);
10618 	if( n < 0 ){
10619 		/* Invalid length */
10620 		jx9_result_int(pCtx, -1);
10621 		return JX9_OK;
10622 	}
10623 	/* Perform the comparison */
10624 	z1 = jx9_value_to_string(apArg[0], 0);
10625 	z2 = jx9_value_to_string(apArg[1], 0);
10626 	res = SyStrnicmp(z1, z2, (sxu32)n);
10627 	/* Comparison result */
10628 	jx9_result_int(pCtx, res);
10629 	return JX9_OK;
10630 }
10631 /*
10632  * Implode context [i.e: it's private data].
10633  * A pointer to the following structure is forwarded
10634  * verbatim to the array walker callback defined below.
10635  */
10636 struct implode_data {
10637 	jx9_context *pCtx;    /* Call context */
10638 	int bRecursive;       /* TRUE if recursive implode [this is a symisc eXtension] */
10639 	const char *zSep;     /* Arguments separator if any */
10640 	int nSeplen;          /* Separator length */
10641 	int bFirst;           /* TRUE if first call */
10642 	int nRecCount;        /* Recursion count to avoid infinite loop */
10643 };
10644 /*
10645  * Implode walker callback for the [jx9_array_walk()] interface.
10646  * The following routine is invoked for each array entry passed
10647  * to the implode() function.
10648  */
implode_callback(jx9_value * pKey,jx9_value * pValue,void * pUserData)10649 static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
10650 {
10651 	struct implode_data *pData = (struct implode_data *)pUserData;
10652 	const char *zData;
10653 	int nLen;
10654 	if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
10655 		if( pData->nSeplen > 0 ){
10656 			if( !pData->bFirst ){
10657 				/* append the separator first */
10658 				jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
10659 			}else{
10660 				pData->bFirst = 0;
10661 			}
10662 		}
10663 		/* Recurse */
10664 		pData->bFirst = 1;
10665 		pData->nRecCount++;
10666 		jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
10667 		pData->nRecCount--;
10668 		return JX9_OK;
10669 	}
10670 	/* Extract the string representation of the entry value */
10671 	zData = jx9_value_to_string(pValue, &nLen);
10672 	if( nLen > 0 ){
10673 		if( pData->nSeplen > 0 ){
10674 			if( !pData->bFirst ){
10675 				/* append the separator first */
10676 				jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
10677 			}else{
10678 				pData->bFirst = 0;
10679 			}
10680 		}
10681 		jx9_result_string(pData->pCtx, zData, nLen);
10682 	}else{
10683 		SXUNUSED(pKey); /* cc warning */
10684 	}
10685 	return JX9_OK;
10686 }
10687 /*
10688  * string implode(string $glue, array $pieces, ...)
10689  * string implode(array $pieces, ...)
10690  *  Join array elements with a string.
10691  * $glue
10692  *   Defaults to an empty string. This is not the preferred usage of implode() as glue
10693  *   would be the second parameter and thus, the bad prototype would be used.
10694  * $pieces
10695  *   The array of strings to implode.
10696  * Return
10697  *  Returns a string containing a string representation of all the array elements in the same
10698  *  order, with the glue string between each element.
10699  */
jx9Builtin_implode(jx9_context * pCtx,int nArg,jx9_value ** apArg)10700 static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10701 {
10702 	struct implode_data imp_data;
10703 	int i = 1;
10704 	if( nArg < 1 ){
10705 		/* Missing argument, return NULL */
10706 		jx9_result_null(pCtx);
10707 		return JX9_OK;
10708 	}
10709 	/* Prepare the implode context */
10710 	imp_data.pCtx = pCtx;
10711 	imp_data.bRecursive = 0;
10712 	imp_data.bFirst = 1;
10713 	imp_data.nRecCount = 0;
10714 	if( !jx9_value_is_json_array(apArg[0]) ){
10715 		imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
10716 	}else{
10717 		imp_data.zSep = 0;
10718 		imp_data.nSeplen = 0;
10719 		i = 0;
10720 	}
10721 	jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
10722 	/* Start the 'join' process */
10723 	while( i < nArg ){
10724 		if( jx9_value_is_json_array(apArg[i]) ){
10725 			/* Iterate throw array entries */
10726 			jx9_array_walk(apArg[i], implode_callback, &imp_data);
10727 		}else{
10728 			const char *zData;
10729 			int nLen;
10730 			/* Extract the string representation of the jx9 value */
10731 			zData = jx9_value_to_string(apArg[i], &nLen);
10732 			if( nLen > 0 ){
10733 				if( imp_data.nSeplen > 0 ){
10734 					if( !imp_data.bFirst ){
10735 						/* append the separator first */
10736 						jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
10737 					}else{
10738 						imp_data.bFirst = 0;
10739 					}
10740 				}
10741 				jx9_result_string(pCtx, zData, nLen);
10742 			}
10743 		}
10744 		i++;
10745 	}
10746 	return JX9_OK;
10747 }
10748 /*
10749  * string implode_recursive(string $glue, array $pieces, ...)
10750  * Purpose
10751  *  Same as implode() but recurse on arrays.
10752  * Example:
10753  *   $a = array('usr', array('home', 'dean'));
10754  *   print implode_recursive("/", $a);
10755  *   Will output
10756  *     usr/home/dean.
10757  *   While the standard implode would produce.
10758  *    usr/Array.
10759  * Parameter
10760  *  Refer to implode().
10761  * Return
10762  *  Refer to implode().
10763  */
jx9Builtin_implode_recursive(jx9_context * pCtx,int nArg,jx9_value ** apArg)10764 static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
10765 {
10766 	struct implode_data imp_data;
10767 	int i = 1;
10768 	if( nArg < 1 ){
10769 		/* Missing argument, return NULL */
10770 		jx9_result_null(pCtx);
10771 		return JX9_OK;
10772 	}
10773 	/* Prepare the implode context */
10774 	imp_data.pCtx = pCtx;
10775 	imp_data.bRecursive = 1;
10776 	imp_data.bFirst = 1;
10777 	imp_data.nRecCount = 0;
10778 	if( !jx9_value_is_json_array(apArg[0]) ){
10779 		imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
10780 	}else{
10781 		imp_data.zSep = 0;
10782 		imp_data.nSeplen = 0;
10783 		i = 0;
10784 	}
10785 	jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
10786 	/* Start the 'join' process */
10787 	while( i < nArg ){
10788 		if( jx9_value_is_json_array(apArg[i]) ){
10789 			/* Iterate throw array entries */
10790 			jx9_array_walk(apArg[i], implode_callback, &imp_data);
10791 		}else{
10792 			const char *zData;
10793 			int nLen;
10794 			/* Extract the string representation of the jx9 value */
10795 			zData = jx9_value_to_string(apArg[i], &nLen);
10796 			if( nLen > 0 ){
10797 				if( imp_data.nSeplen > 0 ){
10798 					if( !imp_data.bFirst ){
10799 						/* append the separator first */
10800 						jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
10801 					}else{
10802 						imp_data.bFirst = 0;
10803 					}
10804 				}
10805 				jx9_result_string(pCtx, zData, nLen);
10806 			}
10807 		}
10808 		i++;
10809 	}
10810 	return JX9_OK;
10811 }
10812 /*
10813  * array explode(string $delimiter, string $string[, int $limit ])
10814  *  Returns an array of strings, each of which is a substring of string
10815  *  formed by splitting it on boundaries formed by the string delimiter.
10816  * Parameters
10817  *  $delimiter
10818  *   The boundary string.
10819  * $string
10820  *   The input string.
10821  * $limit
10822  *   If limit is set and positive, the returned array will contain a maximum
10823  *   of limit elements with the last element containing the rest of string.
10824  *   If the limit parameter is negative, all fields except the last -limit are returned.
10825  *   If the limit parameter is zero, then this is treated as 1.
10826  * Returns
10827  *  Returns an array of strings created by splitting the string parameter
10828  *  on boundaries formed by the delimiter.
10829  *  If delimiter is an empty string (""), explode() will return FALSE.
10830  *  If delimiter contains a value that is not contained in string and a negative
10831  *  limit is used, then an empty array will be returned, otherwise an array containing string
10832  *  will be returned.
10833  * NOTE:
10834  *  Negative limit is not supported.
10835  */
jx9Builtin_explode(jx9_context * pCtx,int nArg,jx9_value ** apArg)10836 static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
10837 {
10838 	const char *zDelim, *zString, *zCur, *zEnd;
10839 	int nDelim, nStrlen, iLimit;
10840 	jx9_value *pArray;
10841 	jx9_value *pValue;
10842 	sxu32 nOfft;
10843 	sxi32 rc;
10844 	if( nArg < 2 ){
10845 		/* Missing arguments, return FALSE */
10846 		jx9_result_bool(pCtx, 0);
10847 		return JX9_OK;
10848 	}
10849 	/* Extract the delimiter */
10850 	zDelim = jx9_value_to_string(apArg[0], &nDelim);
10851 	if( nDelim < 1 ){
10852 		/* Empty delimiter, return FALSE */
10853 		jx9_result_bool(pCtx, 0);
10854 		return JX9_OK;
10855 	}
10856 	/* Extract the string */
10857 	zString = jx9_value_to_string(apArg[1], &nStrlen);
10858 	if( nStrlen < 1 ){
10859 		/* Empty delimiter, return FALSE */
10860 		jx9_result_bool(pCtx, 0);
10861 		return JX9_OK;
10862 	}
10863 	/* Point to the end of the string */
10864 	zEnd = &zString[nStrlen];
10865 	/* Create the array */
10866 	pArray =  jx9_context_new_array(pCtx);
10867 	pValue = jx9_context_new_scalar(pCtx);
10868 	if( pArray == 0 || pValue == 0 ){
10869 		/* Out of memory, return FALSE */
10870 		jx9_result_bool(pCtx, 0);
10871 		return JX9_OK;
10872 	}
10873 	/* Set a defualt limit */
10874 	iLimit = SXI32_HIGH;
10875 	if( nArg > 2 ){
10876 		iLimit = jx9_value_to_int(apArg[2]);
10877 		 if( iLimit < 0 ){
10878 			iLimit = -iLimit;
10879 		}
10880 		if( iLimit == 0 ){
10881 			iLimit = 1;
10882 		}
10883 		iLimit--;
10884 	}
10885 	/* Start exploding */
10886 	for(;;){
10887 		if( zString >= zEnd ){
10888 			/* No more entry to process */
10889 			break;
10890 		}
10891 		rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
10892 		if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
10893 			/* Limit reached, insert the rest of the string and break */
10894 			if( zEnd > zString ){
10895 				jx9_value_string(pValue, zString, (int)(zEnd-zString));
10896 				jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
10897 			}
10898 			break;
10899 		}
10900 		/* Point to the desired offset */
10901 		zCur = &zString[nOfft];
10902 		if( zCur > zString ){
10903 			/* Perform the store operation */
10904 			jx9_value_string(pValue, zString, (int)(zCur-zString));
10905 			jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
10906 		}
10907 		/* Point beyond the delimiter */
10908 		zString = &zCur[nDelim];
10909 		/* Reset the cursor */
10910 		jx9_value_reset_string_cursor(pValue);
10911 	}
10912 	/* Return the freshly created array */
10913 	jx9_result_value(pCtx, pArray);
10914 	/* NOTE that every allocated jx9_value will be automatically
10915 	 * released as soon we return from this foregin function.
10916 	 */
10917 	return JX9_OK;
10918 }
10919 /*
10920  * string trim(string $str[, string $charlist ])
10921  *  Strip whitespace (or other characters) from the beginning and end of a string.
10922  * Parameters
10923  *  $str
10924  *   The string that will be trimmed.
10925  * $charlist
10926  *   Optionally, the stripped characters can also be specified using the charlist parameter.
10927  *   Simply list all characters that you want to be stripped.
10928  *   With .. you can specify a range of characters.
10929  * Returns.
10930  *  Thr processed string.
10931  */
jx9Builtin_trim(jx9_context * pCtx,int nArg,jx9_value ** apArg)10932 static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
10933 {
10934 	const char *zString;
10935 	int nLen;
10936 	if( nArg < 1 ){
10937 		/* Missing arguments, return null */
10938 		jx9_result_null(pCtx);
10939 		return JX9_OK;
10940 	}
10941 	/* Extract the target string */
10942 	zString = jx9_value_to_string(apArg[0], &nLen);
10943 	if( nLen < 1 ){
10944 		/* Empty string, return */
10945 		jx9_result_string(pCtx, "", 0);
10946 		return JX9_OK;
10947 	}
10948 	/* Start the trim process */
10949 	if( nArg < 2 ){
10950 		SyString sStr;
10951 		/* Remove white spaces and NUL bytes */
10952 		SyStringInitFromBuf(&sStr, zString, nLen);
10953 		SyStringFullTrimSafe(&sStr);
10954 		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
10955 	}else{
10956 		/* Char list */
10957 		const char *zList;
10958 		int nListlen;
10959 		zList = jx9_value_to_string(apArg[1], &nListlen);
10960 		if( nListlen < 1 ){
10961 			/* Return the string unchanged */
10962 			jx9_result_string(pCtx, zString, nLen);
10963 		}else{
10964 			const char *zEnd = &zString[nLen];
10965 			const char *zCur = zString;
10966 			const char *zPtr;
10967 			int i;
10968 			/* Left trim */
10969 			for(;;){
10970 				if( zCur >= zEnd ){
10971 					break;
10972 				}
10973 				zPtr = zCur;
10974 				for( i = 0 ; i < nListlen ; i++ ){
10975 					if( zCur < zEnd && zCur[0] == zList[i] ){
10976 						zCur++;
10977 					}
10978 				}
10979 				if( zCur == zPtr ){
10980 					/* No match, break immediately */
10981 					break;
10982 				}
10983 			}
10984 			/* Right trim */
10985 			zEnd--;
10986 			for(;;){
10987 				if( zEnd <= zCur ){
10988 					break;
10989 				}
10990 				zPtr = zEnd;
10991 				for( i = 0 ; i < nListlen ; i++ ){
10992 					if( zEnd > zCur && zEnd[0] == zList[i] ){
10993 						zEnd--;
10994 					}
10995 				}
10996 				if( zEnd == zPtr ){
10997 					break;
10998 				}
10999 			}
11000 			if( zCur >= zEnd ){
11001 				/* Return the empty string */
11002 				jx9_result_string(pCtx, "", 0);
11003 			}else{
11004 				zEnd++;
11005 				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11006 			}
11007 		}
11008 	}
11009 	return JX9_OK;
11010 }
11011 /*
11012  * string rtrim(string $str[, string $charlist ])
11013  *  Strip whitespace (or other characters) from the end of a string.
11014  * Parameters
11015  *  $str
11016  *   The string that will be trimmed.
11017  * $charlist
11018  *   Optionally, the stripped characters can also be specified using the charlist parameter.
11019  *   Simply list all characters that you want to be stripped.
11020  *   With .. you can specify a range of characters.
11021  * Returns.
11022  *  Thr processed string.
11023  */
jx9Builtin_rtrim(jx9_context * pCtx,int nArg,jx9_value ** apArg)11024 static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
11025 {
11026 	const char *zString;
11027 	int nLen;
11028 	if( nArg < 1 ){
11029 		/* Missing arguments, return null */
11030 		jx9_result_null(pCtx);
11031 		return JX9_OK;
11032 	}
11033 	/* Extract the target string */
11034 	zString = jx9_value_to_string(apArg[0], &nLen);
11035 	if( nLen < 1 ){
11036 		/* Empty string, return */
11037 		jx9_result_string(pCtx, "", 0);
11038 		return JX9_OK;
11039 	}
11040 	/* Start the trim process */
11041 	if( nArg < 2 ){
11042 		SyString sStr;
11043 		/* Remove white spaces and NUL bytes*/
11044 		SyStringInitFromBuf(&sStr, zString, nLen);
11045 		SyStringRightTrimSafe(&sStr);
11046 		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
11047 	}else{
11048 		/* Char list */
11049 		const char *zList;
11050 		int nListlen;
11051 		zList = jx9_value_to_string(apArg[1], &nListlen);
11052 		if( nListlen < 1 ){
11053 			/* Return the string unchanged */
11054 			jx9_result_string(pCtx, zString, nLen);
11055 		}else{
11056 			const char *zEnd = &zString[nLen - 1];
11057 			const char *zCur = zString;
11058 			const char *zPtr;
11059 			int i;
11060 			/* Right trim */
11061 			for(;;){
11062 				if( zEnd <= zCur ){
11063 					break;
11064 				}
11065 				zPtr = zEnd;
11066 				for( i = 0 ; i < nListlen ; i++ ){
11067 					if( zEnd > zCur && zEnd[0] == zList[i] ){
11068 						zEnd--;
11069 					}
11070 				}
11071 				if( zEnd == zPtr ){
11072 					break;
11073 				}
11074 			}
11075 			if( zEnd <= zCur ){
11076 				/* Return the empty string */
11077 				jx9_result_string(pCtx, "", 0);
11078 			}else{
11079 				zEnd++;
11080 				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11081 			}
11082 		}
11083 	}
11084 	return JX9_OK;
11085 }
11086 /*
11087  * string ltrim(string $str[, string $charlist ])
11088  *  Strip whitespace (or other characters) from the beginning and end of a string.
11089  * Parameters
11090  *  $str
11091  *   The string that will be trimmed.
11092  * $charlist
11093  *   Optionally, the stripped characters can also be specified using the charlist parameter.
11094  *   Simply list all characters that you want to be stripped.
11095  *   With .. you can specify a range of characters.
11096  * Returns.
11097  *  The processed string.
11098  */
jx9Builtin_ltrim(jx9_context * pCtx,int nArg,jx9_value ** apArg)11099 static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
11100 {
11101 	const char *zString;
11102 	int nLen;
11103 	if( nArg < 1 ){
11104 		/* Missing arguments, return null */
11105 		jx9_result_null(pCtx);
11106 		return JX9_OK;
11107 	}
11108 	/* Extract the target string */
11109 	zString = jx9_value_to_string(apArg[0], &nLen);
11110 	if( nLen < 1 ){
11111 		/* Empty string, return */
11112 		jx9_result_string(pCtx, "", 0);
11113 		return JX9_OK;
11114 	}
11115 	/* Start the trim process */
11116 	if( nArg < 2 ){
11117 		SyString sStr;
11118 		/* Remove white spaces and NUL byte */
11119 		SyStringInitFromBuf(&sStr, zString, nLen);
11120 		SyStringLeftTrimSafe(&sStr);
11121 		jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
11122 	}else{
11123 		/* Char list */
11124 		const char *zList;
11125 		int nListlen;
11126 		zList = jx9_value_to_string(apArg[1], &nListlen);
11127 		if( nListlen < 1 ){
11128 			/* Return the string unchanged */
11129 			jx9_result_string(pCtx, zString, nLen);
11130 		}else{
11131 			const char *zEnd = &zString[nLen];
11132 			const char *zCur = zString;
11133 			const char *zPtr;
11134 			int i;
11135 			/* Left trim */
11136 			for(;;){
11137 				if( zCur >= zEnd ){
11138 					break;
11139 				}
11140 				zPtr = zCur;
11141 				for( i = 0 ; i < nListlen ; i++ ){
11142 					if( zCur < zEnd && zCur[0] == zList[i] ){
11143 						zCur++;
11144 					}
11145 				}
11146 				if( zCur == zPtr ){
11147 					/* No match, break immediately */
11148 					break;
11149 				}
11150 			}
11151 			if( zCur >= zEnd ){
11152 				/* Return the empty string */
11153 				jx9_result_string(pCtx, "", 0);
11154 			}else{
11155 				jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
11156 			}
11157 		}
11158 	}
11159 	return JX9_OK;
11160 }
11161 /*
11162  * string strtolower(string $str)
11163  *  Make a string lowercase.
11164  * Parameters
11165  *  $str
11166  *   The input string.
11167  * Returns.
11168  *  The lowercased string.
11169  */
jx9Builtin_strtolower(jx9_context * pCtx,int nArg,jx9_value ** apArg)11170 static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
11171 {
11172 	const char *zString, *zCur, *zEnd;
11173 	int nLen;
11174 	if( nArg < 1 ){
11175 		/* Missing arguments, return null */
11176 		jx9_result_null(pCtx);
11177 		return JX9_OK;
11178 	}
11179 	/* Extract the target string */
11180 	zString = jx9_value_to_string(apArg[0], &nLen);
11181 	if( nLen < 1 ){
11182 		/* Empty string, return */
11183 		jx9_result_string(pCtx, "", 0);
11184 		return JX9_OK;
11185 	}
11186 	/* Perform the requested operation */
11187 	zEnd = &zString[nLen];
11188 	for(;;){
11189 		if( zString >= zEnd ){
11190 			/* No more input, break immediately */
11191 			break;
11192 		}
11193 		if( (unsigned char)zString[0] >= 0xc0 ){
11194 			/* UTF-8 stream, output verbatim */
11195 			zCur = zString;
11196 			zString++;
11197 			while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
11198 				zString++;
11199 			}
11200 			/* Append UTF-8 stream */
11201 			jx9_result_string(pCtx, zCur, (int)(zString-zCur));
11202 		}else{
11203 			int c = zString[0];
11204 			if( SyisUpper(c) ){
11205 				c = SyToLower(zString[0]);
11206 			}
11207 			/* Append character */
11208 			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11209 			/* Advance the cursor */
11210 			zString++;
11211 		}
11212 	}
11213 	return JX9_OK;
11214 }
11215 /*
11216  * string strtolower(string $str)
11217  *  Make a string uppercase.
11218  * Parameters
11219  *  $str
11220  *   The input string.
11221  * Returns.
11222  *  The uppercased string.
11223  */
jx9Builtin_strtoupper(jx9_context * pCtx,int nArg,jx9_value ** apArg)11224 static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
11225 {
11226 	const char *zString, *zCur, *zEnd;
11227 	int nLen;
11228 	if( nArg < 1 ){
11229 		/* Missing arguments, return null */
11230 		jx9_result_null(pCtx);
11231 		return JX9_OK;
11232 	}
11233 	/* Extract the target string */
11234 	zString = jx9_value_to_string(apArg[0], &nLen);
11235 	if( nLen < 1 ){
11236 		/* Empty string, return */
11237 		jx9_result_string(pCtx, "", 0);
11238 		return JX9_OK;
11239 	}
11240 	/* Perform the requested operation */
11241 	zEnd = &zString[nLen];
11242 	for(;;){
11243 		if( zString >= zEnd ){
11244 			/* No more input, break immediately */
11245 			break;
11246 		}
11247 		if( (unsigned char)zString[0] >= 0xc0 ){
11248 			/* UTF-8 stream, output verbatim */
11249 			zCur = zString;
11250 			zString++;
11251 			while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
11252 				zString++;
11253 			}
11254 			/* Append UTF-8 stream */
11255 			jx9_result_string(pCtx, zCur, (int)(zString-zCur));
11256 		}else{
11257 			int c = zString[0];
11258 			if( SyisLower(c) ){
11259 				c = SyToUpper(zString[0]);
11260 			}
11261 			/* Append character */
11262 			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11263 			/* Advance the cursor */
11264 			zString++;
11265 		}
11266 	}
11267 	return JX9_OK;
11268 }
11269 /*
11270  * int ord(string $string)
11271  *  Returns the ASCII value of the first character of string.
11272  * Parameters
11273  *  $str
11274  *   The input string.
11275  * Returns.
11276  *  The ASCII value as an integer.
11277  */
jx9Builtin_ord(jx9_context * pCtx,int nArg,jx9_value ** apArg)11278 static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
11279 {
11280 	const char *zString;
11281 	int nLen, c;
11282 	if( nArg < 1 ){
11283 		/* Missing arguments, return -1 */
11284 		jx9_result_int(pCtx, -1);
11285 		return JX9_OK;
11286 	}
11287 	/* Extract the target string */
11288 	zString = jx9_value_to_string(apArg[0], &nLen);
11289 	if( nLen < 1 ){
11290 		/* Empty string, return -1 */
11291 		jx9_result_int(pCtx, -1);
11292 		return JX9_OK;
11293 	}
11294 	/* Extract the ASCII value of the first character */
11295 	c = zString[0];
11296 	/* Return that value */
11297 	jx9_result_int(pCtx, c);
11298 	return JX9_OK;
11299 }
11300 /*
11301  * string chr(int $ascii)
11302  *  Returns a one-character string containing the character specified by ascii.
11303  * Parameters
11304  *  $ascii
11305  *   The ascii code.
11306  * Returns.
11307  *  The specified character.
11308  */
jx9Builtin_chr(jx9_context * pCtx,int nArg,jx9_value ** apArg)11309 static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11310 {
11311 	int c;
11312 	if( nArg < 1 ){
11313 		/* Missing arguments, return null */
11314 		jx9_result_null(pCtx);
11315 		return JX9_OK;
11316 	}
11317 	/* Extract the ASCII value */
11318 	c = jx9_value_to_int(apArg[0]);
11319 	/* Return the specified character */
11320 	jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11321 	return JX9_OK;
11322 }
11323 /*
11324  * Binary to hex consumer callback.
11325  * This callback is the default consumer used by the hash functions
11326  * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
11327  */
HashConsumer(const void * pData,unsigned int nLen,void * pUserData)11328 static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
11329 {
11330 	/* Append hex chunk verbatim */
11331 	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
11332 	return SXRET_OK;
11333 }
11334 /*
11335  * string bin2hex(string $str)
11336  *  Convert binary data into hexadecimal representation.
11337  * Parameters
11338  *  $str
11339  *   The input string.
11340  * Returns.
11341  *  Returns the hexadecimal representation of the given string.
11342  */
jx9Builtin_bin2hex(jx9_context * pCtx,int nArg,jx9_value ** apArg)11343 static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
11344 {
11345 	const char *zString;
11346 	int nLen;
11347 	if( nArg < 1 ){
11348 		/* Missing arguments, return null */
11349 		jx9_result_null(pCtx);
11350 		return JX9_OK;
11351 	}
11352 	/* Extract the target string */
11353 	zString = jx9_value_to_string(apArg[0], &nLen);
11354 	if( nLen < 1 ){
11355 		/* Empty string, return */
11356 		jx9_result_string(pCtx, "", 0);
11357 		return JX9_OK;
11358 	}
11359 	/* Perform the requested operation */
11360 	SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
11361 	return JX9_OK;
11362 }
11363 /* Search callback signature */
11364 typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
11365 /*
11366  * Case-insensitive pattern match.
11367  * Brute force is the default search method used here.
11368  * This is due to the fact that brute-forcing works quite
11369  * well for short/medium texts on modern hardware.
11370  */
iPatternMatch(const void * pText,sxu32 nLen,const void * pPattern,sxu32 iPatLen,sxu32 * pOfft)11371 static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
11372 {
11373 	const char *zpIn = (const char *)pPattern;
11374 	const char *zIn = (const char *)pText;
11375 	const char *zpEnd = &zpIn[iPatLen];
11376 	const char *zEnd = &zIn[nLen];
11377 	const char *zPtr, *zPtr2;
11378 	int c, d;
11379 	if( iPatLen > nLen ){
11380 		/* Don't bother processing */
11381 		return SXERR_NOTFOUND;
11382 	}
11383 	for(;;){
11384 		if( zIn >= zEnd ){
11385 			break;
11386 		}
11387 		c = SyToLower(zIn[0]);
11388 		d = SyToLower(zpIn[0]);
11389 		if( c == d ){
11390 			zPtr   = &zIn[1];
11391 			zPtr2  = &zpIn[1];
11392 			for(;;){
11393 				if( zPtr2 >= zpEnd ){
11394 					/* Pattern found */
11395 					if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
11396 					return SXRET_OK;
11397 				}
11398 				if( zPtr >= zEnd ){
11399 					break;
11400 				}
11401 				c = SyToLower(zPtr[0]);
11402 				d = SyToLower(zPtr2[0]);
11403 				if( c != d ){
11404 					break;
11405 				}
11406 				zPtr++; zPtr2++;
11407 			}
11408 		}
11409 		zIn++;
11410 	}
11411 	/* Pattern not found */
11412 	return SXERR_NOTFOUND;
11413 }
11414 /*
11415  * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
11416  *  Find the first occurrence of a string.
11417  * Parameters
11418  *  $haystack
11419  *   The input string.
11420  * $needle
11421  *   Search pattern (must be a string).
11422  * $before_needle
11423  *   If TRUE, strstr() returns the part of the haystack before the first occurrence
11424  *   of the needle (excluding the needle).
11425  * Return
11426  *  Returns the portion of string, or FALSE if needle is not found.
11427  */
jx9Builtin_strstr(jx9_context * pCtx,int nArg,jx9_value ** apArg)11428 static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11429 {
11430 	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11431 	const char *zBlob, *zPattern;
11432 	int nLen, nPatLen;
11433 	sxu32 nOfft;
11434 	sxi32 rc;
11435 	if( nArg < 2 ){
11436 		/* Missing arguments, return FALSE */
11437 		jx9_result_bool(pCtx, 0);
11438 		return JX9_OK;
11439 	}
11440 	/* Extract the needle and the haystack */
11441 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11442 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11443 	nOfft = 0; /* cc warning */
11444 	if( nLen > 0 && nPatLen > 0 ){
11445 		int before = 0;
11446 		/* Perform the lookup */
11447 		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11448 		if( rc != SXRET_OK ){
11449 			/* Pattern not found, return FALSE */
11450 			jx9_result_bool(pCtx, 0);
11451 			return JX9_OK;
11452 		}
11453 		/* Return the portion of the string */
11454 		if( nArg > 2 ){
11455 			before = jx9_value_to_int(apArg[2]);
11456 		}
11457 		if( before ){
11458 			jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
11459 		}else{
11460 			jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11461 		}
11462 	}else{
11463 		jx9_result_bool(pCtx, 0);
11464 	}
11465 	return JX9_OK;
11466 }
11467 /*
11468  * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
11469  *  Case-insensitive strstr().
11470  * Parameters
11471  *  $haystack
11472  *   The input string.
11473  * $needle
11474  *   Search pattern (must be a string).
11475  * $before_needle
11476  *   If TRUE, strstr() returns the part of the haystack before the first occurrence
11477  *   of the needle (excluding the needle).
11478  * Return
11479  *  Returns the portion of string, or FALSE if needle is not found.
11480  */
jx9Builtin_stristr(jx9_context * pCtx,int nArg,jx9_value ** apArg)11481 static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11482 {
11483 	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11484 	const char *zBlob, *zPattern;
11485 	int nLen, nPatLen;
11486 	sxu32 nOfft;
11487 	sxi32 rc;
11488 	if( nArg < 2 ){
11489 		/* Missing arguments, return FALSE */
11490 		jx9_result_bool(pCtx, 0);
11491 		return JX9_OK;
11492 	}
11493 	/* Extract the needle and the haystack */
11494 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11495 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11496 	nOfft = 0; /* cc warning */
11497 	if( nLen > 0 && nPatLen > 0 ){
11498 		int before = 0;
11499 		/* Perform the lookup */
11500 		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11501 		if( rc != SXRET_OK ){
11502 			/* Pattern not found, return FALSE */
11503 			jx9_result_bool(pCtx, 0);
11504 			return JX9_OK;
11505 		}
11506 		/* Return the portion of the string */
11507 		if( nArg > 2 ){
11508 			before = jx9_value_to_int(apArg[2]);
11509 		}
11510 		if( before ){
11511 			jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
11512 		}else{
11513 			jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11514 		}
11515 	}else{
11516 		jx9_result_bool(pCtx, 0);
11517 	}
11518 	return JX9_OK;
11519 }
11520 /*
11521  * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
11522  *  Returns the numeric position of the first occurrence of needle in the haystack string.
11523  * Parameters
11524  *  $haystack
11525  *   The input string.
11526  * $needle
11527  *   Search pattern (must be a string).
11528  * $offset
11529  *   This optional offset parameter allows you to specify which character in haystack
11530  *   to start searching. The position returned is still relative to the beginning
11531  *   of haystack.
11532  * Return
11533  *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
11534  */
jx9Builtin_strpos(jx9_context * pCtx,int nArg,jx9_value ** apArg)11535 static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11536 {
11537 	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11538 	const char *zBlob, *zPattern;
11539 	int nLen, nPatLen, nStart;
11540 	sxu32 nOfft;
11541 	sxi32 rc;
11542 	if( nArg < 2 ){
11543 		/* Missing arguments, return FALSE */
11544 		jx9_result_bool(pCtx, 0);
11545 		return JX9_OK;
11546 	}
11547 	/* Extract the needle and the haystack */
11548 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11549 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11550 	nOfft = 0; /* cc warning */
11551 	nStart = 0;
11552 	/* Peek the starting offset if available */
11553 	if( nArg > 2 ){
11554 		nStart = jx9_value_to_int(apArg[2]);
11555 		if( nStart < 0 ){
11556 			nStart = -nStart;
11557 		}
11558 		if( nStart >= nLen ){
11559 			/* Invalid offset */
11560 			nStart = 0;
11561 		}else{
11562 			zBlob += nStart;
11563 			nLen -= nStart;
11564 		}
11565 	}
11566 	if( nLen > 0 && nPatLen > 0 ){
11567 		/* Perform the lookup */
11568 		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11569 		if( rc != SXRET_OK ){
11570 			/* Pattern not found, return FALSE */
11571 			jx9_result_bool(pCtx, 0);
11572 			return JX9_OK;
11573 		}
11574 		/* Return the pattern position */
11575 		jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
11576 	}else{
11577 		jx9_result_bool(pCtx, 0);
11578 	}
11579 	return JX9_OK;
11580 }
11581 /*
11582  * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
11583  *  Case-insensitive strpos.
11584  * Parameters
11585  *  $haystack
11586  *   The input string.
11587  * $needle
11588  *   Search pattern (must be a string).
11589  * $offset
11590  *   This optional offset parameter allows you to specify which character in haystack
11591  *   to start searching. The position returned is still relative to the beginning
11592  *   of haystack.
11593  * Return
11594  *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
11595  */
jx9Builtin_stripos(jx9_context * pCtx,int nArg,jx9_value ** apArg)11596 static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11597 {
11598 	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11599 	const char *zBlob, *zPattern;
11600 	int nLen, nPatLen, nStart;
11601 	sxu32 nOfft;
11602 	sxi32 rc;
11603 	if( nArg < 2 ){
11604 		/* Missing arguments, return FALSE */
11605 		jx9_result_bool(pCtx, 0);
11606 		return JX9_OK;
11607 	}
11608 	/* Extract the needle and the haystack */
11609 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11610 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11611 	nOfft = 0; /* cc warning */
11612 	nStart = 0;
11613 	/* Peek the starting offset if available */
11614 	if( nArg > 2 ){
11615 		nStart = jx9_value_to_int(apArg[2]);
11616 		if( nStart < 0 ){
11617 			nStart = -nStart;
11618 		}
11619 		if( nStart >= nLen ){
11620 			/* Invalid offset */
11621 			nStart = 0;
11622 		}else{
11623 			zBlob += nStart;
11624 			nLen -= nStart;
11625 		}
11626 	}
11627 	if( nLen > 0 && nPatLen > 0 ){
11628 		/* Perform the lookup */
11629 		rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
11630 		if( rc != SXRET_OK ){
11631 			/* Pattern not found, return FALSE */
11632 			jx9_result_bool(pCtx, 0);
11633 			return JX9_OK;
11634 		}
11635 		/* Return the pattern position */
11636 		jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
11637 	}else{
11638 		jx9_result_bool(pCtx, 0);
11639 	}
11640 	return JX9_OK;
11641 }
11642 /*
11643  * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
11644  *  Find the numeric position of the last occurrence of needle in the haystack string.
11645  * Parameters
11646  *  $haystack
11647  *   The input string.
11648  * $needle
11649  *   Search pattern (must be a string).
11650  * $offset
11651  *   If specified, search will start this number of characters counted from the beginning
11652  *   of the string. If the value is negative, search will instead start from that many
11653  *   characters from the end of the string, searching backwards.
11654  * Return
11655  *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
11656  */
jx9Builtin_strrpos(jx9_context * pCtx,int nArg,jx9_value ** apArg)11657 static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11658 {
11659 	const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
11660 	ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
11661 	int nLen, nPatLen;
11662 	sxu32 nOfft;
11663 	sxi32 rc;
11664 	if( nArg < 2 ){
11665 		/* Missing arguments, return FALSE */
11666 		jx9_result_bool(pCtx, 0);
11667 		return JX9_OK;
11668 	}
11669 	/* Extract the needle and the haystack */
11670 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11671 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11672 	/* Point to the end of the pattern */
11673 	zPtr = &zBlob[nLen - 1];
11674 	zEnd = &zBlob[nLen];
11675 	/* Save the starting posistion */
11676 	zStart = zBlob;
11677 	nOfft = 0; /* cc warning */
11678 	/* Peek the starting offset if available */
11679 	if( nArg > 2 ){
11680 		int nStart;
11681 		nStart = jx9_value_to_int(apArg[2]);
11682 		if( nStart < 0 ){
11683 			nStart = -nStart;
11684 			if( nStart >= nLen ){
11685 				/* Invalid offset */
11686 				jx9_result_bool(pCtx, 0);
11687 				return JX9_OK;
11688 			}else{
11689 				nLen -= nStart;
11690 				zPtr = &zBlob[nLen - 1];
11691 				zEnd = &zBlob[nLen];
11692 			}
11693 		}else{
11694 			if( nStart >= nLen ){
11695 				/* Invalid offset */
11696 				jx9_result_bool(pCtx, 0);
11697 				return JX9_OK;
11698 			}else{
11699 				zBlob += nStart;
11700 				nLen -= nStart;
11701 			}
11702 		}
11703 	}
11704 	if( nLen > 0 && nPatLen > 0 ){
11705 		/* Perform the lookup */
11706 		for(;;){
11707 			if( zBlob >= zPtr ){
11708 				break;
11709 			}
11710 			rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
11711 			if( rc == SXRET_OK ){
11712 				/* Pattern found, return it's position */
11713 				jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
11714 				return JX9_OK;
11715 			}
11716 			zPtr--;
11717 		}
11718 		/* Pattern not found, return FALSE */
11719 		jx9_result_bool(pCtx, 0);
11720 	}else{
11721 		jx9_result_bool(pCtx, 0);
11722 	}
11723 	return JX9_OK;
11724 }
11725 /*
11726  * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
11727  *  Case-insensitive strrpos.
11728  * Parameters
11729  *  $haystack
11730  *   The input string.
11731  * $needle
11732  *   Search pattern (must be a string).
11733  * $offset
11734  *   If specified, search will start this number of characters counted from the beginning
11735  *   of the string. If the value is negative, search will instead start from that many
11736  *   characters from the end of the string, searching backwards.
11737  * Return
11738  *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
11739  */
jx9Builtin_strripos(jx9_context * pCtx,int nArg,jx9_value ** apArg)11740 static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
11741 {
11742 	const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
11743 	ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
11744 	int nLen, nPatLen;
11745 	sxu32 nOfft;
11746 	sxi32 rc;
11747 	if( nArg < 2 ){
11748 		/* Missing arguments, return FALSE */
11749 		jx9_result_bool(pCtx, 0);
11750 		return JX9_OK;
11751 	}
11752 	/* Extract the needle and the haystack */
11753 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11754 	zPattern = jx9_value_to_string(apArg[1], &nPatLen);
11755 	/* Point to the end of the pattern */
11756 	zPtr = &zBlob[nLen - 1];
11757 	zEnd = &zBlob[nLen];
11758 	/* Save the starting posistion */
11759 	zStart = zBlob;
11760 	nOfft = 0; /* cc warning */
11761 	/* Peek the starting offset if available */
11762 	if( nArg > 2 ){
11763 		int nStart;
11764 		nStart = jx9_value_to_int(apArg[2]);
11765 		if( nStart < 0 ){
11766 			nStart = -nStart;
11767 			if( nStart >= nLen ){
11768 				/* Invalid offset */
11769 				jx9_result_bool(pCtx, 0);
11770 				return JX9_OK;
11771 			}else{
11772 				nLen -= nStart;
11773 				zPtr = &zBlob[nLen - 1];
11774 				zEnd = &zBlob[nLen];
11775 			}
11776 		}else{
11777 			if( nStart >= nLen ){
11778 				/* Invalid offset */
11779 				jx9_result_bool(pCtx, 0);
11780 				return JX9_OK;
11781 			}else{
11782 				zBlob += nStart;
11783 				nLen -= nStart;
11784 			}
11785 		}
11786 	}
11787 	if( nLen > 0 && nPatLen > 0 ){
11788 		/* Perform the lookup */
11789 		for(;;){
11790 			if( zBlob >= zPtr ){
11791 				break;
11792 			}
11793 			rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
11794 			if( rc == SXRET_OK ){
11795 				/* Pattern found, return it's position */
11796 				jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
11797 				return JX9_OK;
11798 			}
11799 			zPtr--;
11800 		}
11801 		/* Pattern not found, return FALSE */
11802 		jx9_result_bool(pCtx, 0);
11803 	}else{
11804 		jx9_result_bool(pCtx, 0);
11805 	}
11806 	return JX9_OK;
11807 }
11808 /*
11809  * int strrchr(string $haystack, mixed $needle)
11810  *  Find the last occurrence of a character in a string.
11811  * Parameters
11812  *  $haystack
11813  *   The input string.
11814  * $needle
11815  *  If needle contains more than one character, only the first is used.
11816  *  This behavior is different from that of strstr().
11817  *  If needle is not a string, it is converted to an integer and applied
11818  *  as the ordinal value of a character.
11819  * Return
11820  *  This function returns the portion of string, or FALSE if needle is not found.
11821  */
jx9Builtin_strrchr(jx9_context * pCtx,int nArg,jx9_value ** apArg)11822 static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
11823 {
11824 	const char *zBlob;
11825 	int nLen, c;
11826 	if( nArg < 2 ){
11827 		/* Missing arguments, return FALSE */
11828 		jx9_result_bool(pCtx, 0);
11829 		return JX9_OK;
11830 	}
11831 	/* Extract the haystack */
11832 	zBlob = jx9_value_to_string(apArg[0], &nLen);
11833 	c = 0; /* cc warning */
11834 	if( nLen > 0 ){
11835 		sxu32 nOfft;
11836 		sxi32 rc;
11837 		if( jx9_value_is_string(apArg[1]) ){
11838 			const char *zPattern;
11839 			zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
11840 														 * for NULL pointer.
11841 														 */
11842 			c = zPattern[0];
11843 		}else{
11844 			/* Int cast */
11845 			c = jx9_value_to_int(apArg[1]);
11846 		}
11847 		/* Perform the lookup */
11848 		rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
11849 		if( rc != SXRET_OK ){
11850 			/* No such entry, return FALSE */
11851 			jx9_result_bool(pCtx, 0);
11852 			return JX9_OK;
11853 		}
11854 		/* Return the string portion */
11855 		jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
11856 	}else{
11857 		jx9_result_bool(pCtx, 0);
11858 	}
11859 	return JX9_OK;
11860 }
11861 /*
11862  * string strrev(string $string)
11863  *  Reverse a string.
11864  * Parameters
11865  *  $string
11866  *   String to be reversed.
11867  * Return
11868  *  The reversed string.
11869  */
jx9Builtin_strrev(jx9_context * pCtx,int nArg,jx9_value ** apArg)11870 static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
11871 {
11872 	const char *zIn, *zEnd;
11873 	int nLen, c;
11874 	if( nArg < 1 ){
11875 		/* Missing arguments, return NULL */
11876 		jx9_result_null(pCtx);
11877 		return JX9_OK;
11878 	}
11879 	/* Extract the target string */
11880 	zIn = jx9_value_to_string(apArg[0], &nLen);
11881 	if( nLen < 1 ){
11882 		/* Empty string Return null */
11883 		jx9_result_null(pCtx);
11884 		return JX9_OK;
11885 	}
11886 	/* Perform the requested operation */
11887 	zEnd = &zIn[nLen - 1];
11888 	for(;;){
11889 		if( zEnd < zIn ){
11890 			/* No more input to process */
11891 			break;
11892 		}
11893 		/* Append current character */
11894 		c = zEnd[0];
11895 		jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
11896 		zEnd--;
11897 	}
11898 	return JX9_OK;
11899 }
11900 /*
11901  * string str_repeat(string $input, int $multiplier)
11902  *  Returns input repeated multiplier times.
11903  * Parameters
11904  *  $string
11905  *   String to be repeated.
11906  * $multiplier
11907  *  Number of time the input string should be repeated.
11908  *  multiplier has to be greater than or equal to 0. If the multiplier is set
11909  *  to 0, the function will return an empty string.
11910  * Return
11911  *  The repeated string.
11912  */
jx9Builtin_str_repeat(jx9_context * pCtx,int nArg,jx9_value ** apArg)11913 static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
11914 {
11915 	const char *zIn;
11916 	int nLen, nMul;
11917 	int rc;
11918 	if( nArg < 2 ){
11919 		/* Missing arguments, return NULL */
11920 		jx9_result_null(pCtx);
11921 		return JX9_OK;
11922 	}
11923 	/* Extract the target string */
11924 	zIn = jx9_value_to_string(apArg[0], &nLen);
11925 	if( nLen < 1 ){
11926 		/* Empty string.Return null */
11927 		jx9_result_null(pCtx);
11928 		return JX9_OK;
11929 	}
11930 	/* Extract the multiplier */
11931 	nMul = jx9_value_to_int(apArg[1]);
11932 	if( nMul < 1 ){
11933 		/* Return the empty string */
11934 		jx9_result_string(pCtx, "", 0);
11935 		return JX9_OK;
11936 	}
11937 	/* Perform the requested operation */
11938 	for(;;){
11939 		if( nMul < 1 ){
11940 			break;
11941 		}
11942 		/* Append the copy */
11943 		rc = jx9_result_string(pCtx, zIn, nLen);
11944 		if( rc != JX9_OK ){
11945 			/* Out of memory, break immediately */
11946 			break;
11947 		}
11948 		nMul--;
11949 	}
11950 	return JX9_OK;
11951 }
11952 /*
11953  * string nl2br(string $string[, bool $is_xhtml = true ])
11954  *  Inserts HTML line breaks before all newlines in a string.
11955  * Parameters
11956  *  $string
11957  *   The input string.
11958  * $is_xhtml
11959  *   Whenever to use XHTML compatible line breaks or not.
11960  * Return
11961  *  The processed string.
11962  */
jx9Builtin_nl2br(jx9_context * pCtx,int nArg,jx9_value ** apArg)11963 static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
11964 {
11965 	const char *zIn, *zCur, *zEnd;
11966 	int is_xhtml = 0;
11967 	int nLen;
11968 	if( nArg < 1 ){
11969 		/* Missing arguments, return the empty string */
11970 		jx9_result_string(pCtx, "", 0);
11971 		return JX9_OK;
11972 	}
11973 	/* Extract the target string */
11974 	zIn = jx9_value_to_string(apArg[0], &nLen);
11975 	if( nLen < 1 ){
11976 		/* Empty string, return null */
11977 		jx9_result_null(pCtx);
11978 		return JX9_OK;
11979 	}
11980 	if( nArg > 1 ){
11981 		is_xhtml = jx9_value_to_bool(apArg[1]);
11982 	}
11983 	zEnd = &zIn[nLen];
11984 	/* Perform the requested operation */
11985 	for(;;){
11986 		zCur = zIn;
11987 		/* Delimit the string */
11988 		while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
11989 			zIn++;
11990 		}
11991 		if( zCur < zIn ){
11992 			/* Output chunk verbatim */
11993 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
11994 		}
11995 		if( zIn >= zEnd ){
11996 			/* No more input to process */
11997 			break;
11998 		}
11999 		/* Output the HTML line break */
12000 		if( is_xhtml ){
12001 			jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
12002 		}else{
12003 			jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
12004 		}
12005 		zCur = zIn;
12006 		/* Append trailing line */
12007 		while( zIn < zEnd && (zIn[0] == '\n'  || zIn[0] == '\r') ){
12008 			zIn++;
12009 		}
12010 		if( zCur < zIn ){
12011 			/* Output chunk verbatim */
12012 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
12013 		}
12014 	}
12015 	return JX9_OK;
12016 }
12017 /*
12018  * Format a given string and invoke the given callback on each processed chunk.
12019  *  According to the JX9 reference manual.
12020  * The format string is composed of zero or more directives: ordinary characters
12021  * (excluding %) that are copied directly to the result, and conversion
12022  * specifications, each of which results in fetching its own parameter.
12023  * This applies to both sprintf() and printf().
12024  * Each conversion specification consists of a percent sign (%), followed by one
12025  * or more of these elements, in order:
12026  *   An optional sign specifier that forces a sign (- or +) to be used on a number.
12027  *   By default, only the - sign is used on a number if it's negative. This specifier forces
12028  *   positive numbers to have the + sign attached as well.
12029  *   An optional padding specifier that says what character will be used for padding
12030  *   the results to the right string size. This may be a space character or a 0 (zero character).
12031  *   The default is to pad with spaces. An alternate padding character can be specified by prefixing
12032  *   it with a single quote ('). See the examples below.
12033  *   An optional alignment specifier that says if the result should be left-justified or right-justified.
12034  *   The default is right-justified; a - character here will make it left-justified.
12035  *   An optional number, a width specifier that says how many characters (minimum) this conversion
12036  *   should result in.
12037  *   An optional precision specifier in the form of a period (`.') followed by an optional decimal
12038  *   digit string that says how many decimal digits should be displayed for floating-point numbers.
12039  *   When using this specifier on a string, it acts as a cutoff point, setting a maximum character
12040  *   limit to the string.
12041  *  A type specifier that says what type the argument data should be treated as. Possible types:
12042  *       % - a literal percent character. No argument is required.
12043  *       b - the argument is treated as an integer, and presented as a binary number.
12044  *       c - the argument is treated as an integer, and presented as the character with that ASCII value.
12045  *       d - the argument is treated as an integer, and presented as a (signed) decimal number.
12046  *       e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands
12047  * 	     for the number of digits after the decimal point.
12048  *       E - like %e but uses uppercase letter (e.g. 1.2E+2).
12049  *       u - the argument is treated as an integer, and presented as an unsigned decimal number.
12050  *       f - the argument is treated as a float, and presented as a floating-point number (locale aware).
12051  *       F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
12052  *       g - shorter of %e and %f.
12053  *       G - shorter of %E and %f.
12054  *       o - the argument is treated as an integer, and presented as an octal number.
12055  *       s - the argument is treated as and presented as a string.
12056  *       x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
12057  *       X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
12058  */
12059 /*
12060  * This implementation is based on the one found in the SQLite3 source tree.
12061  */
12062 #define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
12063 /*
12064 ** Conversion types fall into various categories as defined by the
12065 ** following enumeration.
12066 */
12067 #define JX9_FMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
12068 #define JX9_FMT_FLOAT       2 /* Floating point.%f */
12069 #define JX9_FMT_EXP         3 /* Exponentional notation.%e and %E */
12070 #define JX9_FMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
12071 #define JX9_FMT_SIZE        5 /* Total number of characters processed so far.%n */
12072 #define JX9_FMT_STRING      6 /* Strings.%s */
12073 #define JX9_FMT_PERCENT     7 /* Percent symbol.%% */
12074 #define JX9_FMT_CHARX       8 /* Characters.%c */
12075 #define JX9_FMT_ERROR       9 /* Used to indicate no such conversion type */
12076 /*
12077 ** Allowed values for jx9_fmt_info.flags
12078 */
12079 #define JX9_FMT_FLAG_SIGNED	  0x01
12080 #define JX9_FMT_FLAG_UNSIGNED 0x02
12081 /*
12082 ** Each builtin conversion character (ex: the 'd' in "%d") is described
12083 ** by an instance of the following structure
12084 */
12085 typedef struct jx9_fmt_info jx9_fmt_info;
12086 struct jx9_fmt_info
12087 {
12088   char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
12089   sxu8 base;     /* The base for radix conversion */
12090   int flags;    /* One or more of JX9_FMT_FLAG_ constants below */
12091   sxu8 type;     /* Conversion paradigm */
12092   char *charset; /* The character set for conversion */
12093   char *prefix;  /* Prefix on non-zero values in alt format */
12094 };
12095 #ifndef JX9_OMIT_FLOATING_POINT
12096 /*
12097 ** "*val" is a double such that 0.1 <= *val < 10.0
12098 ** Return the ascii code for the leading digit of *val, then
12099 ** multiply "*val" by 10.0 to renormalize.
12100 **
12101 ** Example:
12102 **     input:     *val = 3.14159
12103 **     output:    *val = 1.4159    function return = '3'
12104 **
12105 ** The counter *cnt is incremented each time.  After counter exceeds
12106 ** 16 (the number of significant digits in a 64-bit float) '0' is
12107 ** always returned.
12108 */
vxGetdigit(sxlongreal * val,int * cnt)12109 static int vxGetdigit(sxlongreal *val, int *cnt)
12110 {
12111   sxlongreal d;
12112   int digit;
12113 
12114   if( (*cnt)++ >= 16 ){
12115 	  return '0';
12116   }
12117   digit = (int)*val;
12118   d = digit;
12119    *val = (*val - d)*10.0;
12120   return digit + '0' ;
12121 }
12122 #endif /* JX9_OMIT_FLOATING_POINT */
12123 /*
12124  * The following table is searched linearly, so it is good to put the most frequently
12125  * used conversion types first.
12126  */
12127 static const jx9_fmt_info aFmt[] = {
12128   {  'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0    },
12129   {  's',  0, 0, JX9_FMT_STRING,     0,                  0    },
12130   {  'c',  0, 0, JX9_FMT_CHARX,      0,                  0    },
12131   {  'x', 16, 0, JX9_FMT_RADIX,      "0123456789abcdef", "x0" },
12132   {  'X', 16, 0, JX9_FMT_RADIX,      "0123456789ABCDEF", "X0" },
12133   {  'b',  2, 0, JX9_FMT_RADIX,      "01",                "b0"},
12134   {  'o',  8, 0, JX9_FMT_RADIX,      "01234567",         "0"  },
12135   {  'u', 10, 0, JX9_FMT_RADIX,      "0123456789",       0    },
12136   {  'f',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    },
12137   {  'F',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    },
12138   {  'e',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "e",    0    },
12139   {  'E',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "E",    0    },
12140   {  'g',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "e",    0    },
12141   {  'G',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "E",    0    },
12142   {  '%',  0, 0, JX9_FMT_PERCENT,    0,                  0    }
12143 };
12144 /*
12145  * Format a given string.
12146  * The root program.  All variations call this core.
12147  * INPUTS:
12148  *   xConsumer   This is a pointer to a function taking four arguments
12149  *            1. A pointer to the call context.
12150  *            2. A pointer to the list of characters to be output
12151  *               (Note, this list is NOT null terminated.)
12152  *            3. An integer number of characters to be output.
12153  *               (Note: This number might be zero.)
12154  *            4. Upper layer private data.
12155  *   zIn       This is the format string, as in the usual print.
12156  *   apArg     This is a pointer to a list of arguments.
12157  */
jx9InputFormat(int (* xConsumer)(jx9_context *,const char *,int,void *),jx9_context * pCtx,const char * zIn,int nByte,int nArg,jx9_value ** apArg,void * pUserData,int vf)12158 JX9_PRIVATE sxi32 jx9InputFormat(
12159 	int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
12160 	jx9_context *pCtx,  /* call context */
12161 	const char *zIn,    /* Format string */
12162 	int nByte,          /* Format string length */
12163 	int nArg,           /* Total argument of the given arguments */
12164 	jx9_value **apArg,  /* User arguments */
12165 	void *pUserData,    /* Last argument to xConsumer() */
12166 	int vf              /* TRUE if called from vfprintf, vsprintf context */
12167 	)
12168 {
12169 	char spaces[] = "                                                  ";
12170 #define etSPACESIZE ((int)sizeof(spaces)-1)
12171 	const char *zCur, *zEnd = &zIn[nByte];
12172 	char *zBuf, zWorker[JX9_FMT_BUFSIZ];       /* Working buffer */
12173 	const jx9_fmt_info *pInfo;  /* Pointer to the appropriate info structure */
12174 	int flag_alternateform; /* True if "#" flag is present */
12175 	int flag_leftjustify;   /* True if "-" flag is present */
12176 	int flag_blanksign;     /* True if " " flag is present */
12177 	int flag_plussign;      /* True if "+" flag is present */
12178 	int flag_zeropad;       /* True if field width constant starts with zero */
12179 	jx9_value *pArg;         /* Current processed argument */
12180 	jx9_int64 iVal;
12181 	int precision;           /* Precision of the current field */
12182 	char *zExtra;
12183 	int c, rc, n;
12184 	int length;              /* Length of the field */
12185 	int prefix;
12186 	sxu8 xtype;              /* Conversion paradigm */
12187 	int width;               /* Width of the current field */
12188 	int idx;
12189 	n = (vf == TRUE) ? 0 : 1;
12190 #define NEXT_ARG	( n < nArg ? apArg[n++] : 0 )
12191 	/* Start the format process */
12192 	for(;;){
12193 		zCur = zIn;
12194 		while( zIn < zEnd && zIn[0] != '%' ){
12195 			zIn++;
12196 		}
12197 		if( zCur < zIn ){
12198 			/* Consume chunk verbatim */
12199 			rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
12200 			if( rc == SXERR_ABORT ){
12201 				/* Callback request an operation abort */
12202 				break;
12203 			}
12204 		}
12205 		if( zIn >= zEnd ){
12206 			/* No more input to process, break immediately */
12207 			break;
12208 		}
12209 		/* Find out what flags are present */
12210 		flag_leftjustify = flag_plussign = flag_blanksign =
12211 			flag_alternateform = flag_zeropad = 0;
12212 		zIn++; /* Jump the precent sign */
12213 		do{
12214 			c = zIn[0];
12215 			switch( c ){
12216 			case '-':   flag_leftjustify = 1;     c = 0;   break;
12217 			case '+':   flag_plussign = 1;        c = 0;   break;
12218 			case ' ':   flag_blanksign = 1;       c = 0;   break;
12219 			case '#':   flag_alternateform = 1;   c = 0;   break;
12220 			case '0':   flag_zeropad = 1;         c = 0;   break;
12221 			case '\'':
12222 				zIn++;
12223 				if( zIn < zEnd ){
12224 					/* An alternate padding character can be specified by prefixing it with a single quote (') */
12225 					c = zIn[0];
12226 					for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
12227 						spaces[idx] = (char)c;
12228 					}
12229 					c = 0;
12230 				}
12231 				break;
12232 			default:                                       break;
12233 			}
12234 		}while( c==0 && (zIn++ < zEnd) );
12235 		/* Get the field width */
12236 		width = 0;
12237 		while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12238 			width = width*10 + (zIn[0] - '0');
12239 			zIn++;
12240 		}
12241 		if( zIn < zEnd && zIn[0] == '$' ){
12242 			/* Position specifer */
12243 			if( width > 0 ){
12244 				n = width;
12245 				if( vf && n > 0 ){
12246 					n--;
12247 				}
12248 			}
12249 			zIn++;
12250 			width = 0;
12251 			if( zIn < zEnd && zIn[0] == '0' ){
12252 				flag_zeropad = 1;
12253 				zIn++;
12254 			}
12255 			while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12256 				width = width*10 + (zIn[0] - '0');
12257 				zIn++;
12258 			}
12259 		}
12260 		if( width > JX9_FMT_BUFSIZ-10 ){
12261 			width = JX9_FMT_BUFSIZ-10;
12262 		}
12263 		/* Get the precision */
12264 		precision = -1;
12265 		if( zIn < zEnd && zIn[0] == '.' ){
12266 			precision = 0;
12267 			zIn++;
12268 			while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
12269 				precision = precision*10 + (zIn[0] - '0');
12270 				zIn++;
12271 			}
12272 		}
12273 		if( zIn >= zEnd ){
12274 			/* No more input */
12275 			break;
12276 		}
12277 		/* Fetch the info entry for the field */
12278 		pInfo = 0;
12279 		xtype = JX9_FMT_ERROR;
12280 		c = zIn[0];
12281 		zIn++; /* Jump the format specifer */
12282 		for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
12283 			if( c==aFmt[idx].fmttype ){
12284 				pInfo = &aFmt[idx];
12285 				xtype = pInfo->type;
12286 				break;
12287 			}
12288 		}
12289 		zBuf = zWorker; /* Point to the working buffer */
12290 		length = 0;
12291 		zExtra = 0;
12292 		 /*
12293 		  ** At this point, variables are initialized as follows:
12294 		  **
12295 		  **   flag_alternateform          TRUE if a '#' is present.
12296 		  **   flag_plussign               TRUE if a '+' is present.
12297 		  **   flag_leftjustify            TRUE if a '-' is present or if the
12298 		  **                               field width was negative.
12299 		  **   flag_zeropad                TRUE if the width began with 0.
12300 		  **                               the conversion character.
12301 		  **   flag_blanksign              TRUE if a ' ' is present.
12302 		  **   width                       The specified field width.  This is
12303 		  **                               always non-negative.  Zero is the default.
12304 		  **   precision                   The specified precision.  The default
12305 		  **                               is -1.
12306 		  */
12307 		switch(xtype){
12308 		case JX9_FMT_PERCENT:
12309 			/* A literal percent character */
12310 			zWorker[0] = '%';
12311 			length = (int)sizeof(char);
12312 			break;
12313 		case JX9_FMT_CHARX:
12314 			/* The argument is treated as an integer, and presented as the character
12315 			 * with that ASCII value
12316 			 */
12317 			pArg = NEXT_ARG;
12318 			if( pArg == 0 ){
12319 				c = 0;
12320 			}else{
12321 				c = jx9_value_to_int(pArg);
12322 			}
12323 			/* NUL byte is an acceptable value */
12324 			zWorker[0] = (char)c;
12325 			length = (int)sizeof(char);
12326 			break;
12327 		case JX9_FMT_STRING:
12328 			/* the argument is treated as and presented as a string */
12329 			pArg = NEXT_ARG;
12330 			if( pArg == 0 ){
12331 				length = 0;
12332 			}else{
12333 				zBuf = (char *)jx9_value_to_string(pArg, &length);
12334 			}
12335 			if( length < 1 ){
12336 				zBuf = " ";
12337 				length = (int)sizeof(char);
12338 			}
12339 			if( precision>=0 && precision<length ){
12340 				length = precision;
12341 			}
12342 			if( flag_zeropad ){
12343 				/* zero-padding works on strings too */
12344 				for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
12345 					spaces[idx] = '0';
12346 				}
12347 			}
12348 			break;
12349 		case JX9_FMT_RADIX:
12350 			pArg = NEXT_ARG;
12351 			if( pArg == 0 ){
12352 				iVal = 0;
12353 			}else{
12354 				iVal = jx9_value_to_int64(pArg);
12355 			}
12356 			/* Limit the precision to prevent overflowing buf[] during conversion */
12357 			if( precision>JX9_FMT_BUFSIZ-40 ){
12358 				precision = JX9_FMT_BUFSIZ-40;
12359 			}
12360 #if 1
12361         /* For the format %#x, the value zero is printed "0" not "0x0".
12362         ** I think this is stupid.*/
12363         if( iVal==0 ) flag_alternateform = 0;
12364 #else
12365         /* More sensible: turn off the prefix for octal (to prevent "00"),
12366         ** but leave the prefix for hex.*/
12367         if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
12368 #endif
12369         if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
12370           if( iVal<0 ){
12371             iVal = -iVal;
12372 			/* Ticket 1433-003 */
12373 			if( iVal < 0 ){
12374 				/* Overflow */
12375 				iVal= 0x7FFFFFFFFFFFFFFF;
12376 			}
12377             prefix = '-';
12378           }else if( flag_plussign )  prefix = '+';
12379           else if( flag_blanksign )  prefix = ' ';
12380           else                       prefix = 0;
12381         }else{
12382 			if( iVal<0 ){
12383 				iVal = -iVal;
12384 				/* Ticket 1433-003 */
12385 				if( iVal < 0 ){
12386 					/* Overflow */
12387 					iVal= 0x7FFFFFFFFFFFFFFF;
12388 				}
12389 			}
12390 			prefix = 0;
12391 		}
12392         if( flag_zeropad && precision<width-(prefix!=0) ){
12393           precision = width-(prefix!=0);
12394         }
12395         zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
12396         {
12397           register char *cset;      /* Use registers for speed */
12398           register int base;
12399           cset = pInfo->charset;
12400           base = pInfo->base;
12401           do{                                           /* Convert to ascii */
12402             *(--zBuf) = cset[iVal%base];
12403             iVal = iVal/base;
12404           }while( iVal>0 );
12405         }
12406         length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
12407         for(idx=precision-length; idx>0; idx--){
12408           *(--zBuf) = '0';                             /* Zero pad */
12409         }
12410         if( prefix ) *(--zBuf) = (char)prefix;               /* Add sign */
12411         if( flag_alternateform && pInfo->prefix ){      /* Add "0" or "0x" */
12412           char *pre, x;
12413           pre = pInfo->prefix;
12414           if( *zBuf!=pre[0] ){
12415             for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
12416           }
12417         }
12418         length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
12419 		break;
12420 		case JX9_FMT_FLOAT:
12421 		case JX9_FMT_EXP:
12422 		case JX9_FMT_GENERIC:{
12423 #ifndef JX9_OMIT_FLOATING_POINT
12424 		long double realvalue;
12425 		int  exp;                /* exponent of real numbers */
12426 		double rounder;          /* Used for rounding floating point values */
12427 		int flag_dp;            /* True if decimal point should be shown */
12428 		int flag_rtz;           /* True if trailing zeros should be removed */
12429 		int flag_exp;           /* True to force display of the exponent */
12430 		int nsd;                 /* Number of significant digits returned */
12431 		pArg = NEXT_ARG;
12432 		if( pArg == 0 ){
12433 			realvalue = 0;
12434 		}else{
12435 			realvalue = jx9_value_to_double(pArg);
12436 		}
12437         if( precision<0 ) precision = 6;         /* Set default precision */
12438         if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
12439         if( realvalue<0.0 ){
12440           realvalue = -realvalue;
12441           prefix = '-';
12442         }else{
12443           if( flag_plussign )          prefix = '+';
12444           else if( flag_blanksign )    prefix = ' ';
12445           else                         prefix = 0;
12446         }
12447         if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
12448         rounder = 0.0;
12449 #if 0
12450         /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
12451         for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
12452 #else
12453         /* It makes more sense to use 0.5 */
12454         for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
12455 #endif
12456         if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
12457         /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
12458         exp = 0;
12459         if( realvalue>0.0 ){
12460           while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
12461           while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
12462           while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
12463           while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
12464           if( exp>350 || exp<-350 ){
12465             zBuf = "NaN";
12466             length = 3;
12467             break;
12468           }
12469         }
12470         zBuf = zWorker;
12471         /*
12472         ** If the field type is etGENERIC, then convert to either etEXP
12473         ** or etFLOAT, as appropriate.
12474         */
12475         flag_exp = xtype==JX9_FMT_EXP;
12476         if( xtype!=JX9_FMT_FLOAT ){
12477           realvalue += rounder;
12478           if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
12479         }
12480         if( xtype==JX9_FMT_GENERIC ){
12481           flag_rtz = !flag_alternateform;
12482           if( exp<-4 || exp>precision ){
12483             xtype = JX9_FMT_EXP;
12484           }else{
12485             precision = precision - exp;
12486             xtype = JX9_FMT_FLOAT;
12487           }
12488         }else{
12489           flag_rtz = 0;
12490         }
12491         /*
12492         ** The "exp+precision" test causes output to be of type etEXP if
12493         ** the precision is too large to fit in buf[].
12494         */
12495         nsd = 0;
12496         if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
12497           flag_dp = (precision>0 || flag_alternateform);
12498           if( prefix ) *(zBuf++) = (char)prefix;         /* Sign */
12499           if( exp<0 )  *(zBuf++) = '0';            /* Digits before "." */
12500           else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12501           if( flag_dp ) *(zBuf++) = '.';           /* The decimal point */
12502           for(exp++; exp<0 && precision>0; precision--, exp++){
12503             *(zBuf++) = '0';
12504           }
12505           while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12506           *(zBuf--) = 0;                           /* Null terminate */
12507           if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
12508             while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
12509             if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
12510           }
12511           zBuf++;                            /* point to next free slot */
12512         }else{    /* etEXP or etGENERIC */
12513           flag_dp = (precision>0 || flag_alternateform);
12514           if( prefix ) *(zBuf++) = (char)prefix;   /* Sign */
12515           *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);  /* First digit */
12516           if( flag_dp ) *(zBuf++) = '.';     /* Decimal point */
12517           while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
12518           zBuf--;                            /* point to last digit */
12519           if( flag_rtz && flag_dp ){          /* Remove tail zeros */
12520             while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
12521             if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
12522           }
12523           zBuf++;                            /* point to next free slot */
12524           if( exp || flag_exp ){
12525             *(zBuf++) = pInfo->charset[0];
12526             if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
12527             else       { *(zBuf++) = '+'; }
12528             if( exp>=100 ){
12529               *(zBuf++) = (char)((exp/100)+'0');                /* 100's digit */
12530               exp %= 100;
12531             }
12532             *(zBuf++) = (char)(exp/10+'0');                     /* 10's digit */
12533             *(zBuf++) = (char)(exp%10+'0');                     /* 1's digit */
12534           }
12535         }
12536         /* The converted number is in buf[] and zero terminated.Output it.
12537         ** Note that the number is in the usual order, not reversed as with
12538         ** integer conversions.*/
12539         length = (int)(zBuf-zWorker);
12540         zBuf = zWorker;
12541         /* Special case:  Add leading zeros if the flag_zeropad flag is
12542         ** set and we are not left justified */
12543         if( flag_zeropad && !flag_leftjustify && length < width){
12544           int i;
12545           int nPad = width - length;
12546           for(i=width; i>=nPad; i--){
12547             zBuf[i] = zBuf[i-nPad];
12548           }
12549           i = prefix!=0;
12550           while( nPad-- ) zBuf[i++] = '0';
12551           length = width;
12552         }
12553 #else
12554          zBuf = " ";
12555 		 length = (int)sizeof(char);
12556 #endif /* JX9_OMIT_FLOATING_POINT */
12557 		 break;
12558 							 }
12559 		default:
12560 			/* Invalid format specifer */
12561 			zWorker[0] = '?';
12562 			length = (int)sizeof(char);
12563 			break;
12564 		}
12565 		 /*
12566 		 ** The text of the conversion is pointed to by "zBuf" and is
12567 		 ** "length" characters long.The field width is "width".Do
12568 		 ** the output.
12569 		 */
12570     if( !flag_leftjustify ){
12571       register int nspace;
12572       nspace = width-length;
12573       if( nspace>0 ){
12574         while( nspace>=etSPACESIZE ){
12575 			rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
12576 			if( rc != SXRET_OK ){
12577 				return SXERR_ABORT; /* Consumer routine request an operation abort */
12578 			}
12579 			nspace -= etSPACESIZE;
12580         }
12581         if( nspace>0 ){
12582 			rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
12583 			if( rc != SXRET_OK ){
12584 				return SXERR_ABORT; /* Consumer routine request an operation abort */
12585 			}
12586 		}
12587       }
12588     }
12589     if( length>0 ){
12590 		rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
12591 		if( rc != SXRET_OK ){
12592 		  return SXERR_ABORT; /* Consumer routine request an operation abort */
12593 		}
12594     }
12595     if( flag_leftjustify ){
12596       register int nspace;
12597       nspace = width-length;
12598       if( nspace>0 ){
12599         while( nspace>=etSPACESIZE ){
12600 			rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
12601 			if( rc != SXRET_OK ){
12602 				return SXERR_ABORT; /* Consumer routine request an operation abort */
12603 			}
12604 			nspace -= etSPACESIZE;
12605         }
12606         if( nspace>0 ){
12607 			rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
12608 			if( rc != SXRET_OK ){
12609 				return SXERR_ABORT; /* Consumer routine request an operation abort */
12610 			}
12611 		}
12612       }
12613     }
12614  }/* for(;;) */
12615 	return SXRET_OK;
12616 }
12617 /*
12618  * Callback [i.e: Formatted input consumer] of the sprintf function.
12619  */
sprintfConsumer(jx9_context * pCtx,const char * zInput,int nLen,void * pUserData)12620 static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
12621 {
12622 	/* Consume directly */
12623 	jx9_result_string(pCtx, zInput, nLen);
12624 	SXUNUSED(pUserData); /* cc warning */
12625 	return JX9_OK;
12626 }
12627 /*
12628  * string sprintf(string $format[, mixed $args [, mixed $... ]])
12629  *  Return a formatted string.
12630  * Parameters
12631  *  $format
12632  *    The format string (see block comment above)
12633  * Return
12634  *  A string produced according to the formatting string format.
12635  */
jx9Builtin_sprintf(jx9_context * pCtx,int nArg,jx9_value ** apArg)12636 static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12637 {
12638 	const char *zFormat;
12639 	int nLen;
12640 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
12641 		/* Missing/Invalid arguments, return the empty string */
12642 		jx9_result_string(pCtx, "", 0);
12643 		return JX9_OK;
12644 	}
12645 	/* Extract the string format */
12646 	zFormat = jx9_value_to_string(apArg[0], &nLen);
12647 	if( nLen < 1 ){
12648 		/* Empty string */
12649 		jx9_result_string(pCtx, "", 0);
12650 		return JX9_OK;
12651 	}
12652 	/* Format the string */
12653 	jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
12654 	return JX9_OK;
12655 }
12656 /*
12657  * Callback [i.e: Formatted input consumer] of the printf function.
12658  */
printfConsumer(jx9_context * pCtx,const char * zInput,int nLen,void * pUserData)12659 static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
12660 {
12661 	jx9_int64 *pCounter = (jx9_int64 *)pUserData;
12662 	/* Call the VM output consumer directly */
12663 	jx9_context_output(pCtx, zInput, nLen);
12664 	/* Increment counter */
12665 	*pCounter += nLen;
12666 	return JX9_OK;
12667 }
12668 /*
12669  * int64 printf(string $format[, mixed $args[, mixed $... ]])
12670  *  Output a formatted string.
12671  * Parameters
12672  *  $format
12673  *   See sprintf() for a description of format.
12674  * Return
12675  *  The length of the outputted string.
12676  */
jx9Builtin_printf(jx9_context * pCtx,int nArg,jx9_value ** apArg)12677 static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12678 {
12679 	jx9_int64 nCounter = 0;
12680 	const char *zFormat;
12681 	int nLen;
12682 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
12683 		/* Missing/Invalid arguments, return 0 */
12684 		jx9_result_int(pCtx, 0);
12685 		return JX9_OK;
12686 	}
12687 	/* Extract the string format */
12688 	zFormat = jx9_value_to_string(apArg[0], &nLen);
12689 	if( nLen < 1 ){
12690 		/* Empty string */
12691 		jx9_result_int(pCtx, 0);
12692 		return JX9_OK;
12693 	}
12694 	/* Format the string */
12695 	jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
12696 	/* Return the length of the outputted string */
12697 	jx9_result_int64(pCtx, nCounter);
12698 	return JX9_OK;
12699 }
12700 /*
12701  * int vprintf(string $format, array $args)
12702  *  Output a formatted string.
12703  * Parameters
12704  *  $format
12705  *   See sprintf() for a description of format.
12706  * Return
12707  *  The length of the outputted string.
12708  */
jx9Builtin_vprintf(jx9_context * pCtx,int nArg,jx9_value ** apArg)12709 static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12710 {
12711 	jx9_int64 nCounter = 0;
12712 	const char *zFormat;
12713 	jx9_hashmap *pMap;
12714 	SySet sArg;
12715 	int nLen, n;
12716 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
12717 		/* Missing/Invalid arguments, return 0 */
12718 		jx9_result_int(pCtx, 0);
12719 		return JX9_OK;
12720 	}
12721 	/* Extract the string format */
12722 	zFormat = jx9_value_to_string(apArg[0], &nLen);
12723 	if( nLen < 1 ){
12724 		/* Empty string */
12725 		jx9_result_int(pCtx, 0);
12726 		return JX9_OK;
12727 	}
12728 	/* Point to the hashmap */
12729 	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
12730 	/* Extract arguments from the hashmap */
12731 	n = jx9HashmapValuesToSet(pMap, &sArg);
12732 	/* Format the string */
12733 	jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
12734 	/* Return the length of the outputted string */
12735 	jx9_result_int64(pCtx, nCounter);
12736 	/* Release the container */
12737 	SySetRelease(&sArg);
12738 	return JX9_OK;
12739 }
12740 /*
12741  * int vsprintf(string $format, array $args)
12742  *  Output a formatted string.
12743  * Parameters
12744  *  $format
12745  *   See sprintf() for a description of format.
12746  * Return
12747  *  A string produced according to the formatting string format.
12748  */
jx9Builtin_vsprintf(jx9_context * pCtx,int nArg,jx9_value ** apArg)12749 static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
12750 {
12751 	const char *zFormat;
12752 	jx9_hashmap *pMap;
12753 	SySet sArg;
12754 	int nLen, n;
12755 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
12756 		/* Missing/Invalid arguments, return the empty string */
12757 		jx9_result_string(pCtx, "", 0);
12758 		return JX9_OK;
12759 	}
12760 	/* Extract the string format */
12761 	zFormat = jx9_value_to_string(apArg[0], &nLen);
12762 	if( nLen < 1 ){
12763 		/* Empty string */
12764 		jx9_result_string(pCtx, "", 0);
12765 		return JX9_OK;
12766 	}
12767 	/* Point to hashmap */
12768 	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
12769 	/* Extract arguments from the hashmap */
12770 	n = jx9HashmapValuesToSet(pMap, &sArg);
12771 	/* Format the string */
12772 	jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
12773 	/* Release the container */
12774 	SySetRelease(&sArg);
12775 	return JX9_OK;
12776 }
12777 /*
12778  * string size_format(int64 $size)
12779  *  Return a smart string represenation of the given size [i.e: 64-bit integer]
12780  *  Example:
12781  *    print size_format(1*1024*1024*1024);// 1GB
12782  *    print size_format(512*1024*1024); // 512 MB
12783  *    print size_format(file_size(/path/to/my/file_8192)); //8KB
12784  * Parameter
12785  *  $size
12786  *    Entity size in bytes.
12787  * Return
12788  *   Formatted string representation of the given size.
12789  */
jx9Builtin_size_format(jx9_context * pCtx,int nArg,jx9_value ** apArg)12790 static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
12791 {
12792 	/*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
12793 	static const char zUnit[] = {"KMGTPEZ"};
12794 	sxi32 nRest, i_32;
12795 	jx9_int64 iSize;
12796 	int c = -1; /* index in zUnit[] */
12797 
12798 	if( nArg < 1 ){
12799 		/* Missing argument, return the empty string */
12800 		jx9_result_string(pCtx, "", 0);
12801 		return JX9_OK;
12802 	}
12803 	/* Extract the given size */
12804 	iSize = jx9_value_to_int64(apArg[0]);
12805 	if( iSize < 100 /* Bytes */ ){
12806 		/* Don't bother formatting, return immediately */
12807 		jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
12808 		return JX9_OK;
12809 	}
12810 	for(;;){
12811 		nRest = (sxi32)(iSize & 0x3FF);
12812 		iSize >>= 10;
12813 		c++;
12814 		if( (iSize & (~0 ^ 1023)) == 0 ){
12815 			break;
12816 		}
12817 	}
12818 	nRest /= 100;
12819 	if( nRest > 9 ){
12820 		nRest = 9;
12821 	}
12822 	if( iSize > 999 ){
12823 		c++;
12824 		nRest = 9;
12825 		iSize = 0;
12826 	}
12827 	i_32 = (sxi32)iSize;
12828 	/* Format */
12829 	jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
12830 	return JX9_OK;
12831 }
12832 #if !defined(JX9_DISABLE_HASH_FUNC)
12833 /*
12834  * string md5(string $str[, bool $raw_output = false])
12835  *   Calculate the md5 hash of a string.
12836  * Parameter
12837  *  $str
12838  *   Input string
12839  * $raw_output
12840  *   If the optional raw_output is set to TRUE, then the md5 digest
12841  *   is instead returned in raw binary format with a length of 16.
12842  * Return
12843  *  MD5 Hash as a 32-character hexadecimal string.
12844  */
jx9Builtin_md5(jx9_context * pCtx,int nArg,jx9_value ** apArg)12845 static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
12846 {
12847 	unsigned char zDigest[16];
12848 	int raw_output = FALSE;
12849 	const void *pIn;
12850 	int nLen;
12851 	if( nArg < 1 ){
12852 		/* Missing arguments, return the empty string */
12853 		jx9_result_string(pCtx, "", 0);
12854 		return JX9_OK;
12855 	}
12856 	/* Extract the input string */
12857 	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
12858 	if( nLen < 1 ){
12859 		/* Empty string */
12860 		jx9_result_string(pCtx, "", 0);
12861 		return JX9_OK;
12862 	}
12863 	if( nArg > 1 && jx9_value_is_bool(apArg[1])){
12864 		raw_output = jx9_value_to_bool(apArg[1]);
12865 	}
12866 	/* Compute the MD5 digest */
12867 	SyMD5Compute(pIn, (sxu32)nLen, zDigest);
12868 	if( raw_output ){
12869 		/* Output raw digest */
12870 		jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
12871 	}else{
12872 		/* Perform a binary to hex conversion */
12873 		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
12874 	}
12875 	return JX9_OK;
12876 }
12877 /*
12878  * string sha1(string $str[, bool $raw_output = false])
12879  *   Calculate the sha1 hash of a string.
12880  * Parameter
12881  *  $str
12882  *   Input string
12883  * $raw_output
12884  *   If the optional raw_output is set to TRUE, then the md5 digest
12885  *   is instead returned in raw binary format with a length of 16.
12886  * Return
12887  *  SHA1 Hash as a 40-character hexadecimal string.
12888  */
jx9Builtin_sha1(jx9_context * pCtx,int nArg,jx9_value ** apArg)12889 static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
12890 {
12891 	unsigned char zDigest[20];
12892 	int raw_output = FALSE;
12893 	const void *pIn;
12894 	int nLen;
12895 	if( nArg < 1 ){
12896 		/* Missing arguments, return the empty string */
12897 		jx9_result_string(pCtx, "", 0);
12898 		return JX9_OK;
12899 	}
12900 	/* Extract the input string */
12901 	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
12902 	if( nLen < 1 ){
12903 		/* Empty string */
12904 		jx9_result_string(pCtx, "", 0);
12905 		return JX9_OK;
12906 	}
12907 	if( nArg > 1 && jx9_value_is_bool(apArg[1])){
12908 		raw_output = jx9_value_to_bool(apArg[1]);
12909 	}
12910 	/* Compute the SHA1 digest */
12911 	SySha1Compute(pIn, (sxu32)nLen, zDigest);
12912 	if( raw_output ){
12913 		/* Output raw digest */
12914 		jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
12915 	}else{
12916 		/* Perform a binary to hex conversion */
12917 		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
12918 	}
12919 	return JX9_OK;
12920 }
12921 /*
12922  * int64 crc32(string $str)
12923  *   Calculates the crc32 polynomial of a strin.
12924  * Parameter
12925  *  $str
12926  *   Input string
12927  * Return
12928  *  CRC32 checksum of the given input (64-bit integer).
12929  */
jx9Builtin_crc32(jx9_context * pCtx,int nArg,jx9_value ** apArg)12930 static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
12931 {
12932 	const void *pIn;
12933 	sxu32 nCRC;
12934 	int nLen;
12935 	if( nArg < 1 ){
12936 		/* Missing arguments, return 0 */
12937 		jx9_result_int(pCtx, 0);
12938 		return JX9_OK;
12939 	}
12940 	/* Extract the input string */
12941 	pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
12942 	if( nLen < 1 ){
12943 		/* Empty string */
12944 		jx9_result_int(pCtx, 0);
12945 		return JX9_OK;
12946 	}
12947 	/* Calculate the sum */
12948 	nCRC = SyCrc32(pIn, (sxu32)nLen);
12949 	/* Return the CRC32 as 64-bit integer */
12950 	jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
12951 	return JX9_OK;
12952 }
12953 #endif /* JX9_DISABLE_HASH_FUNC */
12954 /*
12955  * Parse a CSV string and invoke the supplied callback for each processed xhunk.
12956  */
jx9ProcessCsv(const char * zInput,int nByte,int delim,int encl,int escape,sxi32 (* xConsumer)(const char *,int,void *),void * pUserData)12957 JX9_PRIVATE sxi32 jx9ProcessCsv(
12958 	const char *zInput, /* Raw input */
12959 	int nByte,  /* Input length */
12960 	int delim,  /* Delimiter */
12961 	int encl,   /* Enclosure */
12962 	int escape,  /* Escape character */
12963 	sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
12964 	void *pUserData /* Last argument to xConsumer() */
12965 	)
12966 {
12967 	const char *zEnd = &zInput[nByte];
12968 	const char *zIn = zInput;
12969 	const char *zPtr;
12970 	int isEnc;
12971 	/* Start processing */
12972 	for(;;){
12973 		if( zIn >= zEnd ){
12974 			/* No more input to process */
12975 			break;
12976 		}
12977 		isEnc = 0;
12978 		zPtr = zIn;
12979 		/* Find the first delimiter */
12980 		while( zIn < zEnd ){
12981 			if( zIn[0] == delim && !isEnc){
12982 				/* Delimiter found, break imediately */
12983 				break;
12984 			}else if( zIn[0] == encl ){
12985 				/* Inside enclosure? */
12986 				isEnc = !isEnc;
12987 			}else if( zIn[0] == escape ){
12988 				/* Escape sequence */
12989 				zIn++;
12990 			}
12991 			/* Advance the cursor */
12992 			zIn++;
12993 		}
12994 		if( zIn > zPtr ){
12995 			int nByte = (int)(zIn-zPtr);
12996 			sxi32 rc;
12997 			/* Invoke the supllied callback */
12998 			if( zPtr[0] == encl ){
12999 				zPtr++;
13000 				nByte-=2;
13001 			}
13002 			if( nByte > 0 ){
13003 				rc = xConsumer(zPtr, nByte, pUserData);
13004 				if( rc == SXERR_ABORT ){
13005 					/* User callback request an operation abort */
13006 					break;
13007 				}
13008 			}
13009 		}
13010 		/* Ignore trailing delimiter */
13011 		while( zIn < zEnd && zIn[0] == delim ){
13012 			zIn++;
13013 		}
13014 	}
13015 	return SXRET_OK;
13016 }
13017 /*
13018  * Default consumer callback for the CSV parsing routine defined above.
13019  * All the processed input is insereted into an array passed as the last
13020  * argument to this callback.
13021  */
jx9CsvConsumer(const char * zToken,int nTokenLen,void * pUserData)13022 JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
13023 {
13024 	jx9_value *pArray = (jx9_value *)pUserData;
13025 	jx9_value sEntry;
13026 	SyString sToken;
13027 	/* Insert the token in the given array */
13028 	SyStringInitFromBuf(&sToken, zToken, nTokenLen);
13029 	/* Remove trailing and leading white spcaces and null bytes */
13030 	SyStringFullTrimSafe(&sToken);
13031 	if( sToken.nByte < 1){
13032 		return SXRET_OK;
13033 	}
13034 	jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
13035 	jx9_array_add_elem(pArray, 0, &sEntry);
13036 	jx9MemObjRelease(&sEntry);
13037 	return SXRET_OK;
13038 }
13039 /*
13040  * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
13041  *  Parse a CSV string into an array.
13042  * Parameters
13043  *  $input
13044  *   The string to parse.
13045  *  $delimiter
13046  *   Set the field delimiter (one character only).
13047  *  $enclosure
13048  *   Set the field enclosure character (one character only).
13049  *  $escape
13050  *   Set the escape character (one character only). Defaults as a backslash (\)
13051  * Return
13052  *  An indexed array containing the CSV fields or NULL on failure.
13053  */
jx9Builtin_str_getcsv(jx9_context * pCtx,int nArg,jx9_value ** apArg)13054 static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
13055 {
13056 	const char *zInput, *zPtr;
13057 	jx9_value *pArray;
13058 	int delim  = ',';   /* Delimiter */
13059 	int encl   = '"' ;  /* Enclosure */
13060 	int escape = '\\';  /* Escape character */
13061 	int nLen;
13062 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
13063 		/* Missing/Invalid arguments, return NULL */
13064 		jx9_result_null(pCtx);
13065 		return JX9_OK;
13066 	}
13067 	/* Extract the raw input */
13068 	zInput = jx9_value_to_string(apArg[0], &nLen);
13069 	if( nArg > 1 ){
13070 		int i;
13071 		if( jx9_value_is_string(apArg[1]) ){
13072 			/* Extract the delimiter */
13073 			zPtr = jx9_value_to_string(apArg[1], &i);
13074 			if( i > 0 ){
13075 				delim = zPtr[0];
13076 			}
13077 		}
13078 		if( nArg > 2 ){
13079 			if( jx9_value_is_string(apArg[2]) ){
13080 				/* Extract the enclosure */
13081 				zPtr = jx9_value_to_string(apArg[2], &i);
13082 				if( i > 0 ){
13083 					encl = zPtr[0];
13084 				}
13085 			}
13086 			if( nArg > 3 ){
13087 				if( jx9_value_is_string(apArg[3]) ){
13088 					/* Extract the escape character */
13089 					zPtr = jx9_value_to_string(apArg[3], &i);
13090 					if( i > 0 ){
13091 						escape = zPtr[0];
13092 					}
13093 				}
13094 			}
13095 		}
13096 	}
13097 	/* Create our array */
13098 	pArray = jx9_context_new_array(pCtx);
13099 	if( pArray == 0 ){
13100 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
13101 		jx9_result_null(pCtx);
13102 		return JX9_OK;
13103 	}
13104 	/* Parse the raw input */
13105 	jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
13106 	/* Return the freshly created array */
13107 	jx9_result_value(pCtx, pArray);
13108 	return JX9_OK;
13109 }
13110 /*
13111  * Extract a tag name from a raw HTML input and insert it in the given
13112  * container.
13113  * Refer to [strip_tags()].
13114  */
AddTag(SySet * pSet,const char * zTag,int nByte)13115 static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
13116 {
13117 	const char *zEnd = &zTag[nByte];
13118 	const char *zPtr;
13119 	SyString sEntry;
13120 	/* Strip tags */
13121 	for(;;){
13122 		while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
13123 			|| zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
13124 				zTag++;
13125 		}
13126 		if( zTag >= zEnd ){
13127 			break;
13128 		}
13129 		zPtr = zTag;
13130 		/* Delimit the tag */
13131 		while(zTag < zEnd ){
13132 			if( (unsigned char)zTag[0] >= 0xc0 ){
13133 				/* UTF-8 stream */
13134 				zTag++;
13135 				SX_JMP_UTF8(zTag, zEnd);
13136 			}else if( !SyisAlphaNum(zTag[0]) ){
13137 				break;
13138 			}else{
13139 				zTag++;
13140 			}
13141 		}
13142 		if( zTag > zPtr ){
13143 			/* Perform the insertion */
13144 			SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
13145 			SyStringFullTrim(&sEntry);
13146 			SySetPut(pSet, (const void *)&sEntry);
13147 		}
13148 		/* Jump the trailing '>' */
13149 		zTag++;
13150 	}
13151 	return SXRET_OK;
13152 }
13153 /*
13154  * Check if the given HTML tag name is present in the given container.
13155  * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
13156  * Refer to [strip_tags()].
13157  */
FindTag(SySet * pSet,const char * zTag,int nByte)13158 static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
13159 {
13160 	if( SySetUsed(pSet) > 0 ){
13161 		const char *zCur, *zEnd = &zTag[nByte];
13162 		SyString sTag;
13163 		while( zTag < zEnd &&  (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
13164 			((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
13165 			zTag++;
13166 		}
13167 		/* Delimit the tag */
13168 		zCur = zTag;
13169 		while(zTag < zEnd ){
13170 			if( (unsigned char)zTag[0] >= 0xc0 ){
13171 				/* UTF-8 stream */
13172 				zTag++;
13173 				SX_JMP_UTF8(zTag, zEnd);
13174 			}else if( !SyisAlphaNum(zTag[0]) ){
13175 				break;
13176 			}else{
13177 				zTag++;
13178 			}
13179 		}
13180 		SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
13181 		/* Trim leading white spaces and null bytes */
13182 		SyStringLeftTrimSafe(&sTag);
13183 		if( sTag.nByte > 0 ){
13184 			SyString *aEntry, *pEntry;
13185 			sxi32 rc;
13186 			sxu32 n;
13187 			/* Perform the lookup */
13188 			aEntry = (SyString *)SySetBasePtr(pSet);
13189 			for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
13190 				pEntry = &aEntry[n];
13191 				/* Do the comparison */
13192 				rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
13193 				if( !rc ){
13194 					return SXRET_OK;
13195 				}
13196 			}
13197 		}
13198 	}
13199 	/* No such tag */
13200 	return SXERR_NOTFOUND;
13201 }
13202 /*
13203  * This function tries to return a string [i.e: in the call context result buffer]
13204  * with all NUL bytes, HTML and JX9 tags stripped from a given string.
13205  * Refer to [strip_tags()].
13206  */
jx9StripTagsFromString(jx9_context * pCtx,const char * zIn,int nByte,const char * zTaglist,int nTaglen)13207 JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
13208 {
13209 	const char *zEnd = &zIn[nByte];
13210 	const char *zPtr, *zTag;
13211 	SySet sSet;
13212 	/* initialize the set of allowed tags */
13213 	SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
13214 	if( nTaglen > 0 ){
13215 		/* Set of allowed tags */
13216 		AddTag(&sSet, zTaglist, nTaglen);
13217 	}
13218 	/* Set the empty string */
13219 	jx9_result_string(pCtx, "", 0);
13220 	/* Start processing */
13221 	for(;;){
13222 		if(zIn >= zEnd){
13223 			/* No more input to process */
13224 			break;
13225 		}
13226 		zPtr = zIn;
13227 		/* Find a tag */
13228 		while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
13229 			zIn++;
13230 		}
13231 		if( zIn > zPtr ){
13232 			/* Consume raw input */
13233 			jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
13234 		}
13235 		/* Ignore trailing null bytes */
13236 		while( zIn < zEnd && zIn[0] == 0 ){
13237 			zIn++;
13238 		}
13239 		if(zIn >= zEnd){
13240 			/* No more input to process */
13241 			break;
13242 		}
13243 		if( zIn[0] == '<' ){
13244 			sxi32 rc;
13245 			zTag = zIn++;
13246 			/* Delimit the tag */
13247 			while( zIn < zEnd && zIn[0] != '>' ){
13248 				zIn++;
13249 			}
13250 			if( zIn < zEnd ){
13251 				zIn++; /* Ignore the trailing closing tag */
13252 			}
13253 			/* Query the set */
13254 			rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
13255 			if( rc == SXRET_OK ){
13256 				/* Keep the tag */
13257 				jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
13258 			}
13259 		}
13260 	}
13261 	/* Cleanup */
13262 	SySetRelease(&sSet);
13263 	return SXRET_OK;
13264 }
13265 /*
13266  * string strip_tags(string $str[, string $allowable_tags])
13267  *   Strip HTML and JX9 tags from a string.
13268  * Parameters
13269  *  $str
13270  *  The input string.
13271  * $allowable_tags
13272  *  You can use the optional second parameter to specify tags which should not be stripped.
13273  * Return
13274  *  Returns the stripped string.
13275  */
jx9Builtin_strip_tags(jx9_context * pCtx,int nArg,jx9_value ** apArg)13276 static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
13277 {
13278 	const char *zTaglist = 0;
13279 	const char *zString;
13280 	int nTaglen = 0;
13281 	int nLen;
13282 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
13283 		/* Missing/Invalid arguments, return the empty string */
13284 		jx9_result_string(pCtx, "", 0);
13285 		return JX9_OK;
13286 	}
13287 	/* Point to the raw string */
13288 	zString = jx9_value_to_string(apArg[0], &nLen);
13289 	if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
13290 		/* Allowed tag */
13291 		zTaglist = jx9_value_to_string(apArg[1], &nTaglen);
13292 	}
13293 	/* Process input */
13294 	jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
13295 	return JX9_OK;
13296 }
13297 /*
13298  * array str_split(string $string[, int $split_length = 1 ])
13299  *  Convert a string to an array.
13300  * Parameters
13301  * $str
13302  *  The input string.
13303  * $split_length
13304  *  Maximum length of the chunk.
13305  * Return
13306  *  If the optional split_length parameter is specified, the returned array
13307  *  will be broken down into chunks with each being split_length in length, otherwise
13308  *  each chunk will be one character in length. FALSE is returned if split_length is less than 1.
13309  *  If the split_length length exceeds the length of string, the entire string is returned
13310  *  as the first (and only) array element.
13311  */
jx9Builtin_str_split(jx9_context * pCtx,int nArg,jx9_value ** apArg)13312 static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
13313 {
13314 	const char *zString, *zEnd;
13315 	jx9_value *pArray, *pValue;
13316 	int split_len;
13317 	int nLen;
13318 	if( nArg < 1 ){
13319 		/* Missing arguments, return FALSE */
13320 		jx9_result_bool(pCtx, 0);
13321 		return JX9_OK;
13322 	}
13323 	/* Point to the target string */
13324 	zString = jx9_value_to_string(apArg[0], &nLen);
13325 	if( nLen < 1 ){
13326 		/* Nothing to process, return FALSE */
13327 		jx9_result_bool(pCtx, 0);
13328 		return JX9_OK;
13329 	}
13330 	split_len = (int)sizeof(char);
13331 	if( nArg > 1 ){
13332 		/* Split length */
13333 		split_len = jx9_value_to_int(apArg[1]);
13334 		if( split_len < 1 ){
13335 			/* Invalid length, return FALSE */
13336 			jx9_result_bool(pCtx, 0);
13337 			return JX9_OK;
13338 		}
13339 		if( split_len > nLen ){
13340 			split_len = nLen;
13341 		}
13342 	}
13343 	/* Create the array and the scalar value */
13344 	pArray = jx9_context_new_array(pCtx);
13345 	/*Chunk value */
13346 	pValue = jx9_context_new_scalar(pCtx);
13347 	if( pValue == 0 || pArray == 0 ){
13348 		/* Return FALSE */
13349 		jx9_result_bool(pCtx, 0);
13350 		return JX9_OK;
13351 	}
13352 	/* Point to the end of the string */
13353 	zEnd = &zString[nLen];
13354 	/* Perform the requested operation */
13355 	for(;;){
13356 		int nMax;
13357 		if( zString >= zEnd ){
13358 			/* No more input to process */
13359 			break;
13360 		}
13361 		nMax = (int)(zEnd-zString);
13362 		if( nMax < split_len ){
13363 			split_len = nMax;
13364 		}
13365 		/* Copy the current chunk */
13366 		jx9_value_string(pValue, zString, split_len);
13367 		/* Insert it */
13368 		jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
13369 		/* reset the string cursor */
13370 		jx9_value_reset_string_cursor(pValue);
13371 		/* Update position */
13372 		zString += split_len;
13373 	}
13374 	/*
13375 	 * Return the array.
13376 	 * Don't worry about freeing memory, everything will be automatically released
13377 	 * upon we return from this function.
13378 	 */
13379 	jx9_result_value(pCtx, pArray);
13380 	return JX9_OK;
13381 }
13382 /*
13383  * Tokenize a raw string and extract the first non-space token.
13384  * Refer to [strspn()].
13385  */
ExtractNonSpaceToken(const char ** pzIn,const char * zEnd,SyString * pOut)13386 static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
13387 {
13388 	const char *zIn = *pzIn;
13389 	const char *zPtr;
13390 	/* Ignore leading white spaces */
13391 	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
13392 		zIn++;
13393 	}
13394 	if( zIn >= zEnd ){
13395 		/* End of input */
13396 		return SXERR_EOF;
13397 	}
13398 	zPtr = zIn;
13399 	/* Extract the token */
13400 	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
13401 		zIn++;
13402 	}
13403 	SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
13404 	/* Synchronize pointers */
13405 	*pzIn = zIn;
13406 	/* Return to the caller */
13407 	return SXRET_OK;
13408 }
13409 /*
13410  * Check if the given string contains only characters from the given mask.
13411  * return the longest match.
13412  * Refer to [strspn()].
13413  */
LongestStringMask(const char * zString,int nLen,const char * zMask,int nMaskLen)13414 static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
13415 {
13416 	const char *zEnd = &zString[nLen];
13417 	const char *zIn = zString;
13418 	int i, c;
13419 	for(;;){
13420 		if( zString >= zEnd ){
13421 			break;
13422 		}
13423 		/* Extract current character */
13424 		c = zString[0];
13425 		/* Perform the lookup */
13426 		for( i = 0 ; i < nMaskLen ; i++ ){
13427 			if( c == zMask[i] ){
13428 				/* Character found */
13429 				break;
13430 			}
13431 		}
13432 		if( i >= nMaskLen ){
13433 			/* Character not in the current mask, break immediately */
13434 			break;
13435 		}
13436 		/* Advance cursor */
13437 		zString++;
13438 	}
13439 	/* Longest match */
13440 	return (int)(zString-zIn);
13441 }
13442 /*
13443  * Do the reverse operation of the previous function [i.e: LongestStringMask()].
13444  * Refer to [strcspn()].
13445  */
LongestStringMask2(const char * zString,int nLen,const char * zMask,int nMaskLen)13446 static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
13447 {
13448 	const char *zEnd = &zString[nLen];
13449 	const char *zIn = zString;
13450 	int i, c;
13451 	for(;;){
13452 		if( zString >= zEnd ){
13453 			break;
13454 		}
13455 		/* Extract current character */
13456 		c = zString[0];
13457 		/* Perform the lookup */
13458 		for( i = 0 ; i < nMaskLen ; i++ ){
13459 			if( c == zMask[i] ){
13460 				break;
13461 			}
13462 		}
13463 		if( i < nMaskLen ){
13464 			/* Character in the current mask, break immediately */
13465 			break;
13466 		}
13467 		/* Advance cursor */
13468 		zString++;
13469 	}
13470 	/* Longest match */
13471 	return (int)(zString-zIn);
13472 }
13473 /*
13474  * int strspn(string $str, string $mask[, int $start[, int $length]])
13475  *  Finds the length of the initial segment of a string consisting entirely
13476  *  of characters contained within a given mask.
13477  * Parameters
13478  * $str
13479  *  The input string.
13480  * $mask
13481  *  The list of allowable characters.
13482  * $start
13483  *  The position in subject to start searching.
13484  *  If start is given and is non-negative, then strspn() will begin examining
13485  *  subject at the start'th position. For instance, in the string 'abcdef', the character
13486  *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
13487  *  If start is given and is negative, then strspn() will begin examining subject at the
13488  *  start'th position from the end of subject.
13489  * $length
13490  *  The length of the segment from subject to examine.
13491  *  If length is given and is non-negative, then subject will be examined for length
13492  *  characters after the starting position.
13493  *  If lengthis given and is negative, then subject will be examined from the starting
13494  *  position up to length characters from the end of subject.
13495  * Return
13496  * Returns the length of the initial segment of subject which consists entirely of characters
13497  * in mask.
13498  */
jx9Builtin_strspn(jx9_context * pCtx,int nArg,jx9_value ** apArg)13499 static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
13500 {
13501 	const char *zString, *zMask, *zEnd;
13502 	int iMasklen, iLen;
13503 	SyString sToken;
13504 	int iCount = 0;
13505 	int rc;
13506 	if( nArg < 2 ){
13507 		/* Missing agruments, return zero */
13508 		jx9_result_int(pCtx, 0);
13509 		return JX9_OK;
13510 	}
13511 	/* Extract the target string */
13512 	zString = jx9_value_to_string(apArg[0], &iLen);
13513 	/* Extract the mask */
13514 	zMask = jx9_value_to_string(apArg[1], &iMasklen);
13515 	if( iLen < 1 || iMasklen < 1 ){
13516 		/* Nothing to process, return zero */
13517 		jx9_result_int(pCtx, 0);
13518 		return JX9_OK;
13519 	}
13520 	if( nArg > 2 ){
13521 		int nOfft;
13522 		/* Extract the offset */
13523 		nOfft = jx9_value_to_int(apArg[2]);
13524 		if( nOfft < 0 ){
13525 			const char *zBase = &zString[iLen + nOfft];
13526 			if( zBase > zString ){
13527 				iLen = (int)(&zString[iLen]-zBase);
13528 				zString = zBase;
13529 			}else{
13530 				/* Invalid offset */
13531 				jx9_result_int(pCtx, 0);
13532 				return JX9_OK;
13533 			}
13534 		}else{
13535 			if( nOfft >= iLen ){
13536 				/* Invalid offset */
13537 				jx9_result_int(pCtx, 0);
13538 				return JX9_OK;
13539 			}else{
13540 				/* Update offset */
13541 				zString += nOfft;
13542 				iLen -= nOfft;
13543 			}
13544 		}
13545 		if( nArg > 3 ){
13546 			int iUserlen;
13547 			/* Extract the desired length */
13548 			iUserlen = jx9_value_to_int(apArg[3]);
13549 			if( iUserlen > 0 && iUserlen < iLen ){
13550 				iLen = iUserlen;
13551 			}
13552 		}
13553 	}
13554 	/* Point to the end of the string */
13555 	zEnd = &zString[iLen];
13556 	/* Extract the first non-space token */
13557 	rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
13558 	if( rc == SXRET_OK && sToken.nByte > 0 ){
13559 		/* Compare against the current mask */
13560 		iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
13561 	}
13562 	/* Longest match */
13563 	jx9_result_int(pCtx, iCount);
13564 	return JX9_OK;
13565 }
13566 /*
13567  * int strcspn(string $str, string $mask[, int $start[, int $length]])
13568  *  Find length of initial segment not matching mask.
13569  * Parameters
13570  * $str
13571  *  The input string.
13572  * $mask
13573  *  The list of not allowed characters.
13574  * $start
13575  *  The position in subject to start searching.
13576  *  If start is given and is non-negative, then strspn() will begin examining
13577  *  subject at the start'th position. For instance, in the string 'abcdef', the character
13578  *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
13579  *  If start is given and is negative, then strspn() will begin examining subject at the
13580  *  start'th position from the end of subject.
13581  * $length
13582  *  The length of the segment from subject to examine.
13583  *  If length is given and is non-negative, then subject will be examined for length
13584  *  characters after the starting position.
13585  *  If lengthis given and is negative, then subject will be examined from the starting
13586  *  position up to length characters from the end of subject.
13587  * Return
13588  *  Returns the length of the segment as an integer.
13589  */
jx9Builtin_strcspn(jx9_context * pCtx,int nArg,jx9_value ** apArg)13590 static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
13591 {
13592 	const char *zString, *zMask, *zEnd;
13593 	int iMasklen, iLen;
13594 	SyString sToken;
13595 	int iCount = 0;
13596 	int rc;
13597 	if( nArg < 2 ){
13598 		/* Missing agruments, return zero */
13599 		jx9_result_int(pCtx, 0);
13600 		return JX9_OK;
13601 	}
13602 	/* Extract the target string */
13603 	zString = jx9_value_to_string(apArg[0], &iLen);
13604 	/* Extract the mask */
13605 	zMask = jx9_value_to_string(apArg[1], &iMasklen);
13606 	if( iLen < 1 ){
13607 		/* Nothing to process, return zero */
13608 		jx9_result_int(pCtx, 0);
13609 		return JX9_OK;
13610 	}
13611 	if( iMasklen < 1 ){
13612 		/* No given mask, return the string length */
13613 		jx9_result_int(pCtx, iLen);
13614 		return JX9_OK;
13615 	}
13616 	if( nArg > 2 ){
13617 		int nOfft;
13618 		/* Extract the offset */
13619 		nOfft = jx9_value_to_int(apArg[2]);
13620 		if( nOfft < 0 ){
13621 			const char *zBase = &zString[iLen + nOfft];
13622 			if( zBase > zString ){
13623 				iLen = (int)(&zString[iLen]-zBase);
13624 				zString = zBase;
13625 			}else{
13626 				/* Invalid offset */
13627 				jx9_result_int(pCtx, 0);
13628 				return JX9_OK;
13629 			}
13630 		}else{
13631 			if( nOfft >= iLen ){
13632 				/* Invalid offset */
13633 				jx9_result_int(pCtx, 0);
13634 				return JX9_OK;
13635 			}else{
13636 				/* Update offset */
13637 				zString += nOfft;
13638 				iLen -= nOfft;
13639 			}
13640 		}
13641 		if( nArg > 3 ){
13642 			int iUserlen;
13643 			/* Extract the desired length */
13644 			iUserlen = jx9_value_to_int(apArg[3]);
13645 			if( iUserlen > 0 && iUserlen < iLen ){
13646 				iLen = iUserlen;
13647 			}
13648 		}
13649 	}
13650 	/* Point to the end of the string */
13651 	zEnd = &zString[iLen];
13652 	/* Extract the first non-space token */
13653 	rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
13654 	if( rc == SXRET_OK && sToken.nByte > 0 ){
13655 		/* Compare against the current mask */
13656 		iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
13657 	}
13658 	/* Longest match */
13659 	jx9_result_int(pCtx, iCount);
13660 	return JX9_OK;
13661 }
13662 /*
13663  * string strpbrk(string $haystack, string $char_list)
13664  *  Search a string for any of a set of characters.
13665  * Parameters
13666  *  $haystack
13667  *   The string where char_list is looked for.
13668  *  $char_list
13669  *   This parameter is case sensitive.
13670  * Return
13671  *  Returns a string starting from the character found, or FALSE if it is not found.
13672  */
jx9Builtin_strpbrk(jx9_context * pCtx,int nArg,jx9_value ** apArg)13673 static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
13674 {
13675 	const char *zString, *zList, *zEnd;
13676 	int iLen, iListLen, i, c;
13677 	sxu32 nOfft, nMax;
13678 	sxi32 rc;
13679 	if( nArg < 2 ){
13680 		/* Missing arguments, return FALSE */
13681 		jx9_result_bool(pCtx, 0);
13682 		return JX9_OK;
13683 	}
13684 	/* Extract the haystack and the char list */
13685 	zString = jx9_value_to_string(apArg[0], &iLen);
13686 	zList = jx9_value_to_string(apArg[1], &iListLen);
13687 	if( iLen < 1 ){
13688 		/* Nothing to process, return FALSE */
13689 		jx9_result_bool(pCtx, 0);
13690 		return JX9_OK;
13691 	}
13692 	/* Point to the end of the string */
13693 	zEnd = &zString[iLen];
13694 	nOfft = nMax = SXU32_HIGH;
13695 	/* perform the requested operation */
13696 	for( i = 0 ; i < iListLen ; i++ ){
13697 		c = zList[i];
13698 		rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
13699 		if( rc == SXRET_OK ){
13700 			if( nMax < nOfft ){
13701 				nOfft = nMax;
13702 			}
13703 		}
13704 	}
13705 	if( nOfft == SXU32_HIGH ){
13706 		/* No such substring, return FALSE */
13707 		jx9_result_bool(pCtx, 0);
13708 	}else{
13709 		/* Return the substring */
13710 		jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
13711 	}
13712 	return JX9_OK;
13713 }
13714 /*
13715  * string soundex(string $str)
13716  *  Calculate the soundex key of a string.
13717  * Parameters
13718  *  $str
13719  *   The input string.
13720  * Return
13721  *  Returns the soundex key as a string.
13722  * Note:
13723  *  This implementation is based on the one found in the SQLite3
13724  * source tree.
13725  */
jx9Builtin_soundex(jx9_context * pCtx,int nArg,jx9_value ** apArg)13726 static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
13727 {
13728 	const unsigned char *zIn;
13729 	char zResult[8];
13730 	int i, j;
13731 	static const unsigned char iCode[] = {
13732 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13733 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13734 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13735 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13736 		0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
13737 		1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
13738 		0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
13739 		1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
13740 	};
13741 	if( nArg < 1 ){
13742 		/* Missing arguments, return the empty string */
13743 		jx9_result_string(pCtx, "", 0);
13744 		return JX9_OK;
13745 	}
13746 	zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
13747 	for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
13748 	if( zIn[i] ){
13749 		unsigned char prevcode = iCode[zIn[i]&0x7f];
13750 		zResult[0] = (char)SyToUpper(zIn[i]);
13751 		for(j=1; j<4 && zIn[i]; i++){
13752 			int code = iCode[zIn[i]&0x7f];
13753 			if( code>0 ){
13754 				if( code!=prevcode ){
13755 					prevcode = (unsigned char)code;
13756 					zResult[j++] = (char)code + '0';
13757 				}
13758 			}else{
13759 				prevcode = 0;
13760 			}
13761 		}
13762 		while( j<4 ){
13763 			zResult[j++] = '0';
13764 		}
13765 		jx9_result_string(pCtx, zResult, 4);
13766 	}else{
13767 	  jx9_result_string(pCtx, "?000", 4);
13768 	}
13769 	return JX9_OK;
13770 }
13771 /*
13772  * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
13773  *  Wraps a string to a given number of characters.
13774  * Parameters
13775  *  $str
13776  *   The input string.
13777  * $width
13778  *  The column width.
13779  * $break
13780  *  The line is broken using the optional break parameter.
13781  * Return
13782  *  Returns the given string wrapped at the specified column.
13783  */
jx9Builtin_wordwrap(jx9_context * pCtx,int nArg,jx9_value ** apArg)13784 static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
13785 {
13786 	const char *zIn, *zEnd, *zBreak;
13787 	int iLen, iBreaklen, iChunk;
13788 	if( nArg < 1 ){
13789 		/* Missing arguments, return the empty string */
13790 		jx9_result_string(pCtx, "", 0);
13791 		return JX9_OK;
13792 	}
13793 	/* Extract the input string */
13794 	zIn = jx9_value_to_string(apArg[0], &iLen);
13795 	if( iLen < 1 ){
13796 		/* Nothing to process, return the empty string */
13797 		jx9_result_string(pCtx, "", 0);
13798 		return JX9_OK;
13799 	}
13800 	/* Chunk length */
13801 	iChunk = 75;
13802 	iBreaklen = 0;
13803 	zBreak = ""; /* cc warning */
13804 	if( nArg > 1 ){
13805 		iChunk = jx9_value_to_int(apArg[1]);
13806 		if( iChunk < 1 ){
13807 			iChunk = 75;
13808 		}
13809 		if( nArg > 2 ){
13810 			zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
13811 		}
13812 	}
13813 	if( iBreaklen < 1 ){
13814 		/* Set a default column break */
13815 #ifdef __WINNT__
13816 		zBreak = "\r\n";
13817 		iBreaklen = (int)sizeof("\r\n")-1;
13818 #else
13819 		zBreak = "\n";
13820 		iBreaklen = (int)sizeof(char);
13821 #endif
13822 	}
13823 	/* Perform the requested operation */
13824 	zEnd = &zIn[iLen];
13825 	for(;;){
13826 		int nMax;
13827 		if( zIn >= zEnd ){
13828 			/* No more input to process */
13829 			break;
13830 		}
13831 		nMax = (int)(zEnd-zIn);
13832 		if( iChunk > nMax ){
13833 			iChunk = nMax;
13834 		}
13835 		/* Append the column first */
13836 		jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
13837 		/* Advance the cursor */
13838 		zIn += iChunk;
13839 		if( zIn < zEnd ){
13840 			/* Append the line break */
13841 			jx9_result_string(pCtx, zBreak, iBreaklen);
13842 		}
13843 	}
13844 	return JX9_OK;
13845 }
13846 /*
13847  * Check if the given character is a member of the given mask.
13848  * Return TRUE on success. FALSE otherwise.
13849  * Refer to [strtok()].
13850  */
CheckMask(int c,const char * zMask,int nMasklen,int * pOfft)13851 static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
13852 {
13853 	int i;
13854 	for( i = 0 ; i < nMasklen ; ++i ){
13855 		if( c == zMask[i] ){
13856 			if( pOfft ){
13857 				*pOfft = i;
13858 			}
13859 			return TRUE;
13860 		}
13861 	}
13862 	return FALSE;
13863 }
13864 /*
13865  * Extract a single token from the input stream.
13866  * Refer to [strtok()].
13867  */
ExtractToken(const char ** pzIn,const char * zEnd,const char * zMask,int nMasklen,SyString * pOut)13868 static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
13869 {
13870 	const char *zIn = *pzIn;
13871 	const char *zPtr;
13872 	/* Ignore leading delimiter */
13873 	while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
13874 		zIn++;
13875 	}
13876 	if( zIn >= zEnd ){
13877 		/* End of input */
13878 		return SXERR_EOF;
13879 	}
13880 	zPtr = zIn;
13881 	/* Extract the token */
13882 	while( zIn < zEnd ){
13883 		if( (unsigned char)zIn[0] >= 0xc0 ){
13884 			/* UTF-8 stream */
13885 			zIn++;
13886 			SX_JMP_UTF8(zIn, zEnd);
13887 		}else{
13888 			if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
13889 				break;
13890 			}
13891 			zIn++;
13892 		}
13893 	}
13894 	SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
13895 	/* Update the cursor */
13896 	*pzIn = zIn;
13897 	/* Return to the caller */
13898 	return SXRET_OK;
13899 }
13900 /* strtok auxiliary private data */
13901 typedef struct strtok_aux_data strtok_aux_data;
13902 struct strtok_aux_data
13903 {
13904 	const char *zDup;  /* Complete duplicate of the input */
13905 	const char *zIn;   /* Current input stream */
13906 	const char *zEnd;  /* End of input */
13907 };
13908 /*
13909  * string strtok(string $str, string $token)
13910  * string strtok(string $token)
13911  *  strtok() splits a string (str) into smaller strings (tokens), with each token
13912  *  being delimited by any character from token. That is, if you have a string like
13913  *  "This is an example string" you could tokenize this string into its individual
13914  *  words by using the space character as the token.
13915  *  Note that only the first call to strtok uses the string argument. Every subsequent
13916  *  call to strtok only needs the token to use, as it keeps track of where it is in
13917  *  the current string. To start over, or to tokenize a new string you simply call strtok
13918  *  with the string argument again to initialize it. Note that you may put multiple tokens
13919  *  in the token parameter. The string will be tokenized when any one of the characters in
13920  *  the argument are found.
13921  * Parameters
13922  *  $str
13923  *  The string being split up into smaller strings (tokens).
13924  * $token
13925  *  The delimiter used when splitting up str.
13926  * Return
13927  *   Current token or FALSE on EOF.
13928  */
jx9Builtin_strtok(jx9_context * pCtx,int nArg,jx9_value ** apArg)13929 static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
13930 {
13931 	strtok_aux_data *pAux;
13932 	const char *zMask;
13933 	SyString sToken;
13934 	int nMasklen;
13935 	sxi32 rc;
13936 	if( nArg < 2 ){
13937 		/* Extract top aux data */
13938 		pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
13939 		if( pAux == 0 ){
13940 			/* No aux data, return FALSE */
13941 			jx9_result_bool(pCtx, 0);
13942 			return JX9_OK;
13943 		}
13944 		nMasklen = 0;
13945 		zMask = ""; /* cc warning */
13946 		if( nArg > 0 ){
13947 			/* Extract the mask */
13948 			zMask = jx9_value_to_string(apArg[0], &nMasklen);
13949 		}
13950 		if( nMasklen < 1 ){
13951 			/* Invalid mask, return FALSE */
13952 			jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
13953 			jx9_context_free_chunk(pCtx, pAux);
13954 			(void)jx9_context_pop_aux_data(pCtx);
13955 			jx9_result_bool(pCtx, 0);
13956 			return JX9_OK;
13957 		}
13958 		/* Extract the token */
13959 		rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
13960 		if( rc != SXRET_OK ){
13961 			/* EOF , discard the aux data */
13962 			jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
13963 			jx9_context_free_chunk(pCtx, pAux);
13964 			(void)jx9_context_pop_aux_data(pCtx);
13965 			jx9_result_bool(pCtx, 0);
13966 		}else{
13967 			/* Return the extracted token */
13968 			jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
13969 		}
13970 	}else{
13971 		const char *zInput, *zCur;
13972 		char *zDup;
13973 		int nLen;
13974 		/* Extract the raw input */
13975 		zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
13976 		if( nLen < 1 ){
13977 			/* Empty input, return FALSE */
13978 			jx9_result_bool(pCtx, 0);
13979 			return JX9_OK;
13980 		}
13981 		/* Extract the mask */
13982 		zMask = jx9_value_to_string(apArg[1], &nMasklen);
13983 		if( nMasklen < 1 ){
13984 			/* Set a default mask */
13985 #define TOK_MASK " \n\t\r\f"
13986 			zMask = TOK_MASK;
13987 			nMasklen = (int)sizeof(TOK_MASK) - 1;
13988 #undef TOK_MASK
13989 		}
13990 		/* Extract a single token */
13991 		rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
13992 		if( rc != SXRET_OK ){
13993 			/* Empty input */
13994 			jx9_result_bool(pCtx, 0);
13995 			return JX9_OK;
13996 		}else{
13997 			/* Return the extracted token */
13998 			jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
13999 		}
14000 		/* Create our auxilliary data and copy the input */
14001 		pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
14002 		if( pAux ){
14003 			nLen -= (int)(zInput-zCur);
14004 			if( nLen < 1 ){
14005 				jx9_context_free_chunk(pCtx, pAux);
14006 				return JX9_OK;
14007 			}
14008 			/* Duplicate input */
14009 			zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
14010 			if( zDup  ){
14011 				SyMemcpy(zInput, zDup, (sxu32)nLen);
14012 				/* Register the aux data */
14013 				pAux->zDup = pAux->zIn = zDup;
14014 				pAux->zEnd = &zDup[nLen];
14015 				jx9_context_push_aux_data(pCtx, pAux);
14016 			}
14017 		}
14018 	}
14019 	return JX9_OK;
14020 }
14021 /*
14022  * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
14023  *  Pad a string to a certain length with another string
14024  * Parameters
14025  *  $input
14026  *   The input string.
14027  * $pad_length
14028  *   If the value of pad_length is negative, less than, or equal to the length of the input
14029  *   string, no padding takes place.
14030  * $pad_string
14031  *   Note:
14032  *    The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
14033  *    divided by the pad_string's length.
14034  * $pad_type
14035  *    Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
14036  *    is not specified it is assumed to be STR_PAD_RIGHT.
14037  * Return
14038  *  The padded string.
14039  */
jx9Builtin_str_pad(jx9_context * pCtx,int nArg,jx9_value ** apArg)14040 static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
14041 {
14042 	int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
14043 	const char *zIn, *zPad;
14044 	if( nArg < 2 ){
14045 		/* Missing arguments, return the empty string */
14046 		jx9_result_string(pCtx, "", 0);
14047 		return JX9_OK;
14048 	}
14049 	/* Extract the target string */
14050 	zIn = jx9_value_to_string(apArg[0], &iLen);
14051 	/* Padding length */
14052 	iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
14053 	if( iPadlen > 0 ){
14054 		iPadlen -= iLen;
14055 	}
14056 	if( iPadlen < 1  ){
14057 		/* Return the string verbatim */
14058 		jx9_result_string(pCtx, zIn, iLen);
14059 		return JX9_OK;
14060 	}
14061 	zPad = " "; /* Whitespace padding */
14062 	iStrpad = (int)sizeof(char);
14063 	iType = 1 ; /* STR_PAD_RIGHT */
14064 	if( nArg > 2 ){
14065 		/* Padding string */
14066 		zPad = jx9_value_to_string(apArg[2], &iStrpad);
14067 		if( iStrpad < 1 ){
14068 			/* Empty string */
14069 			zPad = " "; /* Whitespace padding */
14070 			iStrpad = (int)sizeof(char);
14071 		}
14072 		if( nArg > 3 ){
14073 			/* Padd type */
14074 			iType = jx9_value_to_int(apArg[3]);
14075 			if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
14076 				iType = 1 ; /* STR_PAD_RIGHT */
14077 			}
14078 		}
14079 	}
14080 	iDiv = 1;
14081 	if( iType == 2 ){
14082 		iDiv = 2; /* STR_PAD_BOTH */
14083 	}
14084 	/* Perform the requested operation */
14085 	if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
14086 		jPad = iStrpad;
14087 		for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
14088 			/* Padding */
14089 			if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
14090 				break;
14091 			}
14092 			jx9_result_string(pCtx, zPad, jPad);
14093 		}
14094 		if( iType == 0 /* STR_PAD_LEFT */ ){
14095 			while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
14096 				jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
14097 				if( jPad > iStrpad ){
14098 					jPad = iStrpad;
14099 				}
14100 				if( jPad < 1){
14101 					break;
14102 				}
14103 				jx9_result_string(pCtx, zPad, jPad);
14104 			}
14105 		}
14106 	}
14107 	if( iLen > 0 ){
14108 		/* Append the input string */
14109 		jx9_result_string(pCtx, zIn, iLen);
14110 	}
14111 	if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
14112 		for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
14113 			/* Padding */
14114 			if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
14115 				break;
14116 			}
14117 			jx9_result_string(pCtx, zPad, iStrpad);
14118 		}
14119 		while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
14120 			jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
14121 			if( jPad > iStrpad ){
14122 				jPad = iStrpad;
14123 			}
14124 			if( jPad < 1){
14125 				break;
14126 			}
14127 			jx9_result_string(pCtx, zPad, jPad);
14128 		}
14129 	}
14130 	return JX9_OK;
14131 }
14132 /*
14133  * String replacement private data.
14134  */
14135 typedef struct str_replace_data str_replace_data;
14136 struct str_replace_data
14137 {
14138 	/* The following two fields are only used by the strtr function */
14139 	SyBlob *pWorker;         /* Working buffer */
14140 	ProcStringMatch xMatch;  /* Pattern match routine */
14141 	/* The following two fields are only used by the str_replace function */
14142 	SySet *pCollector;  /* Argument collector*/
14143 	jx9_context *pCtx;  /* Call context */
14144 };
14145 /*
14146  * Remove a substring.
14147  */
14148 #define STRDEL(SRC, SLEN, OFFT, ILEN){\
14149 	for(;;){\
14150 		if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
14151 	}\
14152 }
14153 /*
14154  * Shift right and insert algorithm.
14155  */
14156 #define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
14157 	sxu32 INLEN = LEN - OFFT;\
14158 	for(;;){\
14159 	  if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
14160 	}\
14161 	for(;;){\
14162 		if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
14163 	}\
14164 }
14165 /*
14166  * Replace all occurrences of the search string at offset (nOfft) with the given
14167  * replacement string [i.e: zReplace].
14168  */
StringReplace(SyBlob * pWorker,sxu32 nOfft,int nLen,const char * zReplace,int nReplen)14169 static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
14170 {
14171 	char *zInput = (char *)SyBlobData(pWorker);
14172 	sxu32 n, m;
14173 	n = SyBlobLength(pWorker);
14174 	m = nOfft;
14175 	/* Delete the old entry */
14176 	STRDEL(zInput, n, m, nLen);
14177 	SyBlobLength(pWorker) -= nLen;
14178 	if( nReplen > 0 ){
14179 		sxi32 iRep = nReplen;
14180 		sxi32 rc;
14181 		/*
14182 		 * Make sure the working buffer is big enough to hold the replacement
14183 		 * string.
14184 		 */
14185 		rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
14186 		if( rc != SXRET_OK ){
14187 			/* Simply ignore any memory failure problem */
14188 			return SXRET_OK;
14189 		}
14190 		/* Perform the insertion now */
14191 		zInput = (char *)SyBlobData(pWorker);
14192 		n = SyBlobLength(pWorker);
14193 		SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
14194 		SyBlobLength(pWorker) += nReplen;
14195 	}
14196 	return SXRET_OK;
14197 }
14198 /*
14199  * String replacement walker callback.
14200  * The following callback is invoked for each array entry that hold
14201  * the replace string.
14202  * Refer to the strtr() implementation for more information.
14203  */
StringReplaceWalker(jx9_value * pKey,jx9_value * pData,void * pUserData)14204 static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
14205 {
14206 	str_replace_data *pRepData = (str_replace_data *)pUserData;
14207 	const char *zTarget, *zReplace;
14208 	SyBlob *pWorker;
14209 	int tLen, nLen;
14210 	sxu32 nOfft;
14211 	sxi32 rc;
14212 	/* Point to the working buffer */
14213 	pWorker = pRepData->pWorker;
14214 	if( !jx9_value_is_string(pKey) ){
14215 		/* Target and replace must be a string */
14216 		return JX9_OK;
14217 	}
14218 	/* Extract the target and the replace */
14219 	zTarget = jx9_value_to_string(pKey, &tLen);
14220 	if( tLen < 1 ){
14221 		/* Empty target, return immediately */
14222 		return JX9_OK;
14223 	}
14224 	/* Perform a pattern search */
14225 	rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
14226 	if( rc != SXRET_OK ){
14227 		/* Pattern not found */
14228 		return JX9_OK;
14229 	}
14230 	/* Extract the replace string */
14231 	zReplace = jx9_value_to_string(pData, &nLen);
14232 	/* Perform the replace process */
14233 	StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
14234 	/* All done */
14235 	return JX9_OK;
14236 }
14237 /*
14238  * The following walker callback is invoked by the str_rplace() function inorder
14239  * to collect search/replace string.
14240  * This callback is invoked only if the given argument is of type array.
14241  */
StrReplaceWalker(jx9_value * pKey,jx9_value * pData,void * pUserData)14242 static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
14243 {
14244 	str_replace_data *pRep = (str_replace_data *)pUserData;
14245 	SyString sWorker;
14246 	const char *zIn;
14247 	int nByte;
14248 	/* Extract a string representation of the given argument */
14249 	zIn = jx9_value_to_string(pData, &nByte);
14250 	SyStringInitFromBuf(&sWorker, 0, 0);
14251 	if( nByte > 0 ){
14252 		char *zDup;
14253 		/* Duplicate the chunk */
14254 		zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE,
14255 			TRUE /* Release the chunk automatically, upon this context is destroyd */
14256 			);
14257 		if( zDup == 0 ){
14258 			/* Ignore any memory failure problem */
14259 			jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
14260 			return JX9_OK;
14261 		}
14262 		SyMemcpy(zIn, zDup, (sxu32)nByte);
14263 		/* Save the chunk */
14264 		SyStringInitFromBuf(&sWorker, zDup, nByte);
14265 	}
14266 	/* Save for later processing */
14267 	SySetPut(pRep->pCollector, (const void *)&sWorker);
14268 	/* All done */
14269 	SXUNUSED(pKey); /* cc warning */
14270 	return JX9_OK;
14271 }
14272 /*
14273  * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
14274  * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
14275  *  Replace all occurrences of the search string with the replacement string.
14276  * Parameters
14277  *  If search and replace are arrays, then str_replace() takes a value from each
14278  *  array and uses them to search and replace on subject. If replace has fewer values
14279  *  than search, then an empty string is used for the rest of replacement values.
14280  *  If search is an array and replace is a string, then this replacement string is used
14281  *  for every value of search. The converse would not make sense, though.
14282  *  If search or replace are arrays, their elements are processed first to last.
14283  * $search
14284  *  The value being searched for, otherwise known as the needle. An array may be used
14285  *  to designate multiple needles.
14286  * $replace
14287  *  The replacement value that replaces found search values. An array may be used
14288  *  to designate multiple replacements.
14289  * $subject
14290  *  The string or array being searched and replaced on, otherwise known as the haystack.
14291  *  If subject is an array, then the search and replace is performed with every entry
14292  *  of subject, and the return value is an array as well.
14293  * $count (Not used)
14294  *  If passed, this will be set to the number of replacements performed.
14295  * Return
14296  * This function returns a string or an array with the replaced values.
14297  */
jx9Builtin_str_replace(jx9_context * pCtx,int nArg,jx9_value ** apArg)14298 static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
14299 {
14300 	SyString sTemp, *pSearch, *pReplace;
14301 	ProcStringMatch xMatch;
14302 	const char *zIn, *zFunc;
14303 	str_replace_data sRep;
14304 	SyBlob sWorker;
14305 	SySet sReplace;
14306 	SySet sSearch;
14307 	int rep_str;
14308 	int nByte;
14309 	sxi32 rc;
14310 	if( nArg < 3 ){
14311 		/* Missing/Invalid arguments, return null */
14312 		jx9_result_null(pCtx);
14313 		return JX9_OK;
14314 	}
14315 	/* Initialize fields */
14316 	SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
14317 	SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
14318 	SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
14319 	SyZero(&sRep, sizeof(str_replace_data));
14320 	sRep.pCtx = pCtx;
14321 	sRep.pCollector = &sSearch;
14322 	rep_str = 0;
14323 	/* Extract the subject */
14324 	zIn = jx9_value_to_string(apArg[2], &nByte);
14325 	if( nByte < 1 ){
14326 		/* Nothing to replace, return the empty string */
14327 		jx9_result_string(pCtx, "", 0);
14328 		return JX9_OK;
14329 	}
14330 	/* Copy the subject */
14331 	SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
14332 	/* Search string */
14333 	if( jx9_value_is_json_array(apArg[0]) ){
14334 		/* Collect search string */
14335 		jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
14336 	}else{
14337 		/* Single pattern */
14338 		zIn = jx9_value_to_string(apArg[0], &nByte);
14339 		if( nByte < 1 ){
14340 			/* Return the subject untouched since no search string is available */
14341 			jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
14342 			return JX9_OK;
14343 		}
14344 		SyStringInitFromBuf(&sTemp, zIn, nByte);
14345 		/* Save for later processing */
14346 		SySetPut(&sSearch, (const void *)&sTemp);
14347 	}
14348 	/* Replace string */
14349 	if( jx9_value_is_json_array(apArg[1]) ){
14350 		/* Collect replace string */
14351 		sRep.pCollector = &sReplace;
14352 		jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
14353 	}else{
14354 		/* Single needle */
14355 		zIn = jx9_value_to_string(apArg[1], &nByte);
14356 		rep_str = 1;
14357 		SyStringInitFromBuf(&sTemp, zIn, nByte);
14358 		/* Save for later processing */
14359 		SySetPut(&sReplace, (const void *)&sTemp);
14360 	}
14361 	/* Reset loop cursors */
14362 	SySetResetCursor(&sSearch);
14363 	SySetResetCursor(&sReplace);
14364 	pReplace = pSearch = 0; /* cc warning */
14365 	SyStringInitFromBuf(&sTemp, "", 0);
14366 	/* Extract function name */
14367 	zFunc = jx9_function_name(pCtx);
14368 	/* Set the default pattern match routine */
14369 	xMatch = SyBlobSearch;
14370 	if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) ==  0 ){
14371 		/* Case insensitive pattern match */
14372 		xMatch = iPatternMatch;
14373 	}
14374 	/* Start the replace process */
14375 	while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
14376 		sxu32 nCount, nOfft;
14377 		if( pSearch->nByte <  1 ){
14378 			/* Empty string, ignore */
14379 			continue;
14380 		}
14381 		/* Extract the replace string */
14382 		if( rep_str ){
14383 			pReplace = (SyString *)SySetPeek(&sReplace);
14384 		}else{
14385 			if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
14386 				/* Sepecial case when 'replace set' has fewer values than the search set.
14387 				 * An empty string is used for the rest of replacement values
14388 				 */
14389 				pReplace = 0;
14390 			}
14391 		}
14392 		if( pReplace == 0 ){
14393 			/* Use an empty string instead */
14394 			pReplace = &sTemp;
14395 		}
14396 		nOfft = nCount = 0;
14397 		for(;;){
14398 			if( nCount >= SyBlobLength(&sWorker) ){
14399 				break;
14400 			}
14401 			/* Perform a pattern lookup */
14402 			rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString,
14403 				pSearch->nByte, &nOfft);
14404 			if( rc != SXRET_OK ){
14405 				/* Pattern not found */
14406 				break;
14407 			}
14408 			/* Perform the replace operation */
14409 			StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
14410 			/* Increment offset counter */
14411 			nCount += nOfft + pReplace->nByte;
14412 		}
14413 	}
14414 	/* All done, clean-up the mess left behind */
14415 	jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
14416 	SySetRelease(&sSearch);
14417 	SySetRelease(&sReplace);
14418 	SyBlobRelease(&sWorker);
14419 	return JX9_OK;
14420 }
14421 /*
14422  * string strtr(string $str, string $from, string $to)
14423  * string strtr(string $str, array $replace_pairs)
14424  *  Translate characters or replace substrings.
14425  * Parameters
14426  *  $str
14427  *  The string being translated.
14428  * $from
14429  *  The string being translated to to.
14430  * $to
14431  *  The string replacing from.
14432  * $replace_pairs
14433  *  The replace_pairs parameter may be used instead of to and
14434  *  from, in which case it's an array in the form array('from' => 'to', ...).
14435  * Return
14436  *  The translated string.
14437  *  If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
14438  */
jx9Builtin_strtr(jx9_context * pCtx,int nArg,jx9_value ** apArg)14439 static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
14440 {
14441 	const char *zIn;
14442 	int nLen;
14443 	if( nArg < 1 ){
14444 		/* Nothing to replace, return FALSE */
14445 		jx9_result_bool(pCtx, 0);
14446 		return JX9_OK;
14447 	}
14448 	zIn = jx9_value_to_string(apArg[0], &nLen);
14449 	if( nLen < 1 || nArg < 2 ){
14450 		/* Invalid arguments */
14451 		jx9_result_string(pCtx, zIn, nLen);
14452 		return JX9_OK;
14453 	}
14454 	if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
14455 		str_replace_data sRepData;
14456 		SyBlob sWorker;
14457 		/* Initilaize the working buffer */
14458 		SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
14459 		/* Copy raw string */
14460 		SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
14461 		/* Init our replace data instance */
14462 		sRepData.pWorker = &sWorker;
14463 		sRepData.xMatch = SyBlobSearch;
14464 		/* Iterate throw array entries and perform the replace operation.*/
14465 		jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
14466 		/* All done, return the result string */
14467 		jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker),
14468 			(int)SyBlobLength(&sWorker)); /* Will make it's own copy */
14469 		/* Clean-up */
14470 		SyBlobRelease(&sWorker);
14471 	}else{
14472 		int i, flen, tlen, c, iOfft;
14473 		const char *zFrom, *zTo;
14474 		if( nArg < 3 ){
14475 			/* Nothing to replace */
14476 			jx9_result_string(pCtx, zIn, nLen);
14477 			return JX9_OK;
14478 		}
14479 		/* Extract given arguments */
14480 		zFrom = jx9_value_to_string(apArg[1], &flen);
14481 		zTo = jx9_value_to_string(apArg[2], &tlen);
14482 		if( flen < 1 || tlen < 1 ){
14483 			/* Nothing to replace */
14484 			jx9_result_string(pCtx, zIn, nLen);
14485 			return JX9_OK;
14486 		}
14487 		/* Start the replace process */
14488 		for( i = 0 ; i < nLen ; ++i ){
14489 			c = zIn[i];
14490 			if( CheckMask(c, zFrom, flen, &iOfft) ){
14491 				if ( iOfft < tlen ){
14492 					c = zTo[iOfft];
14493 				}
14494 			}
14495 			jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
14496 
14497 		}
14498 	}
14499 	return JX9_OK;
14500 }
14501 /*
14502  * Parse an INI string.
14503  * According to wikipedia
14504  *  The INI file format is an informal standard for configuration files for some platforms or software.
14505  *  INI files are simple text files with a basic structure composed of "sections" and "properties".
14506  *  Format
14507 *    Properties
14508 *     The basic element contained in an INI file is the property. Every property has a name and a value
14509 *     delimited by an equals sign (=). The name appears to the left of the equals sign.
14510 *     Example:
14511 *      name=value
14512 *    Sections
14513 *     Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
14514 *     in square brackets ([ and ]). All properties after the section declaration are associated with that section.
14515 *     There is no explicit "end of section" delimiter; sections end at the next section declaration
14516 *     or the end of the file. Sections may not be nested.
14517 *     Example:
14518 *      [section]
14519 *   Comments
14520 *    Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
14521 * This function return an array holding parsed values on success.FALSE otherwise.
14522 */
jx9ParseIniString(jx9_context * pCtx,const char * zIn,sxu32 nByte,int bProcessSection)14523 JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
14524 {
14525 	jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
14526 	const char *zCur, *zEnd = &zIn[nByte];
14527 	SyHashEntry *pEntry;
14528 	SyString sEntry;
14529 	SyHash sHash;
14530 	int c;
14531 	/* Create an empty array and worker variables */
14532 	pArray = jx9_context_new_array(pCtx);
14533 	pWorker = jx9_context_new_scalar(pCtx);
14534 	pValue = jx9_context_new_scalar(pCtx);
14535 	if( pArray == 0 || pWorker == 0 || pValue == 0){
14536 		/* Out of memory */
14537 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
14538 		/* Return FALSE */
14539 		jx9_result_bool(pCtx, 0);
14540 		return JX9_OK;
14541 	}
14542 	SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
14543 	pCur = pArray;
14544 	/* Start the parse process */
14545 	for(;;){
14546 		/* Ignore leading white spaces */
14547 		while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
14548 			zIn++;
14549 		}
14550 		if( zIn >= zEnd ){
14551 			/* No more input to process */
14552 			break;
14553 		}
14554 		if( zIn[0] == ';' || zIn[0] == '#' ){
14555 			/* Comment til the end of line */
14556 			zIn++;
14557 			while(zIn < zEnd && zIn[0] != '\n' ){
14558 				zIn++;
14559 			}
14560 			continue;
14561 		}
14562 		/* Reset the string cursor of the working variable */
14563 		jx9_value_reset_string_cursor(pWorker);
14564 		if( zIn[0] == '[' ){
14565 			/* Section: Extract the section name */
14566 			zIn++;
14567 			zCur = zIn;
14568 			while( zIn < zEnd && zIn[0] != ']' ){
14569 				zIn++;
14570 			}
14571 			if( zIn > zCur && bProcessSection ){
14572 				/* Save the section name */
14573 				SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
14574 				SyStringFullTrim(&sEntry);
14575 				jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14576 				if( sEntry.nByte > 0 ){
14577 					/* Associate an array with the section */
14578 					pSection = jx9_context_new_array(pCtx);
14579 					if( pSection ){
14580 						jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
14581 						pCur = pSection;
14582 					}
14583 				}
14584 			}
14585 			zIn++; /* Trailing square brackets ']' */
14586 		}else{
14587 			jx9_value *pOldCur;
14588 			int is_array;
14589 			int iLen;
14590 			/* Properties */
14591 			is_array = 0;
14592 			zCur = zIn;
14593 			iLen = 0; /* cc warning */
14594 			pOldCur = pCur;
14595 			while( zIn < zEnd && zIn[0] != '=' ){
14596 				if( zIn[0] == '[' && !is_array ){
14597 					/* Array */
14598 					iLen = (int)(zIn-zCur);
14599 					is_array = 1;
14600 					if( iLen > 0 ){
14601 						jx9_value *pvArr = 0; /* cc warning */
14602 						/* Query the hashtable */
14603 						SyStringInitFromBuf(&sEntry, zCur, iLen);
14604 						SyStringFullTrim(&sEntry);
14605 						pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
14606 						if( pEntry ){
14607 							pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
14608 						}else{
14609 							/* Create an empty array */
14610 							pvArr = jx9_context_new_array(pCtx);
14611 							if( pvArr ){
14612 								/* Save the entry */
14613 								SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
14614 								/* Insert the entry */
14615 								jx9_value_reset_string_cursor(pWorker);
14616 								jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14617 								jx9_array_add_elem(pCur, pWorker, pvArr);
14618 								jx9_value_reset_string_cursor(pWorker);
14619 							}
14620 						}
14621 						if( pvArr ){
14622 							pCur = pvArr;
14623 						}
14624 					}
14625 					while ( zIn < zEnd && zIn[0] != ']' ){
14626 						zIn++;
14627 					}
14628 				}
14629 				zIn++;
14630 			}
14631 			if( !is_array ){
14632 				iLen = (int)(zIn-zCur);
14633 			}
14634 			/* Trim the key */
14635 			SyStringInitFromBuf(&sEntry, zCur, iLen);
14636 			SyStringFullTrim(&sEntry);
14637 			if( sEntry.nByte > 0 ){
14638 				if( !is_array ){
14639 					/* Save the key name */
14640 					jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
14641 				}
14642 				/* extract key value */
14643 				jx9_value_reset_string_cursor(pValue);
14644 				zIn++; /* '=' */
14645 				while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
14646 					zIn++;
14647 				}
14648 				if( zIn < zEnd ){
14649 					zCur = zIn;
14650 					c = zIn[0];
14651 					if( c == '"' || c == '\'' ){
14652 						zIn++;
14653 						/* Delimit the value */
14654 						while( zIn < zEnd ){
14655 							if ( zIn[0] == c && zIn[-1] != '\\' ){
14656 								break;
14657 							}
14658 							zIn++;
14659 						}
14660 						if( zIn < zEnd ){
14661 							zIn++;
14662 						}
14663 					}else{
14664 						while( zIn < zEnd ){
14665 							if( zIn[0] == '\n' ){
14666 								if( zIn[-1] != '\\' ){
14667 									break;
14668 								}
14669 							}else if( zIn[0] == ';' || zIn[0] == '#' ){
14670 								/* Inline comments */
14671 								break;
14672 							}
14673 							zIn++;
14674 						}
14675 					}
14676 					/* Trim the value */
14677 					SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
14678 					SyStringFullTrim(&sEntry);
14679 					if( c == '"' || c == '\'' ){
14680 						SyStringTrimLeadingChar(&sEntry, c);
14681 						SyStringTrimTrailingChar(&sEntry, c);
14682 					}
14683 					if( sEntry.nByte > 0 ){
14684 						jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
14685 					}
14686 					/* Insert the key and it's value */
14687 					jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
14688 				}
14689 			}else{
14690 				while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
14691 					zIn++;
14692 				}
14693 			}
14694 			pCur = pOldCur;
14695 		}
14696 	}
14697 	SyHashRelease(&sHash);
14698 	/* Return the parse of the INI string */
14699 	jx9_result_value(pCtx, pArray);
14700 	return SXRET_OK;
14701 }
14702 /*
14703  * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
14704  *  Parse a configuration string.
14705  * Parameters
14706  *  $ini
14707  *   The contents of the ini file being parsed.
14708  *  $process_sections
14709  *   By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
14710  *   and settings included. The default for process_sections is FALSE.
14711  *  $scanner_mode (Not used)
14712  *   Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
14713  *   then option values will not be parsed.
14714  * Return
14715  *  The settings are returned as an associative array on success, and FALSE on failure.
14716  */
jx9Builtin_parse_ini_string(jx9_context * pCtx,int nArg,jx9_value ** apArg)14717 static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
14718 {
14719 	const char *zIni;
14720 	int nByte;
14721 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
14722 		/* Missing/Invalid arguments, return FALSE*/
14723 		jx9_result_bool(pCtx, 0);
14724 		return JX9_OK;
14725 	}
14726 	/* Extract the raw INI buffer */
14727 	zIni = jx9_value_to_string(apArg[0], &nByte);
14728 	/* Process the INI buffer*/
14729 	jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
14730 	return JX9_OK;
14731 }
14732 /*
14733  * Ctype Functions.
14734  * Authors:
14735  *    Symisc Systems, devel@symisc.net.
14736  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
14737  * Status:
14738  *    Stable.
14739  */
14740 /*
14741  * bool ctype_alnum(string $text)
14742  *  Checks if all of the characters in the provided string, text, are alphanumeric.
14743  * Parameters
14744  *  $text
14745  *   The tested string.
14746  * Return
14747  *   TRUE if every character in text is either a letter or a digit, FALSE otherwise.
14748  */
jx9Builtin_ctype_alnum(jx9_context * pCtx,int nArg,jx9_value ** apArg)14749 static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
14750 {
14751 	const unsigned char *zIn, *zEnd;
14752 	int nLen;
14753 	if( nArg < 1 ){
14754 		/* Missing arguments, return FALSE */
14755 		jx9_result_bool(pCtx, 0);
14756 		return JX9_OK;
14757 	}
14758 	/* Extract the target string */
14759 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14760 	zEnd = &zIn[nLen];
14761 	if( nLen < 1 ){
14762 		/* Empty string, return FALSE */
14763 		jx9_result_bool(pCtx, 0);
14764 		return JX9_OK;
14765 	}
14766 	/* Perform the requested operation */
14767 	for(;;){
14768 		if( zIn >= zEnd ){
14769 			/* If we reach the end of the string, then the test succeeded. */
14770 			jx9_result_bool(pCtx, 1);
14771 			return JX9_OK;
14772 		}
14773 		if( !SyisAlphaNum(zIn[0]) ){
14774 			break;
14775 		}
14776 		/* Point to the next character */
14777 		zIn++;
14778 	}
14779 	/* The test failed, return FALSE */
14780 	jx9_result_bool(pCtx, 0);
14781 	return JX9_OK;
14782 }
14783 /*
14784  * bool ctype_alpha(string $text)
14785  *  Checks if all of the characters in the provided string, text, are alphabetic.
14786  * Parameters
14787  *  $text
14788  *   The tested string.
14789  * Return
14790  *  TRUE if every character in text is a letter from the current locale, FALSE otherwise.
14791  */
jx9Builtin_ctype_alpha(jx9_context * pCtx,int nArg,jx9_value ** apArg)14792 static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
14793 {
14794 	const unsigned char *zIn, *zEnd;
14795 	int nLen;
14796 	if( nArg < 1 ){
14797 		/* Missing arguments, return FALSE */
14798 		jx9_result_bool(pCtx, 0);
14799 		return JX9_OK;
14800 	}
14801 	/* Extract the target string */
14802 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14803 	zEnd = &zIn[nLen];
14804 	if( nLen < 1 ){
14805 		/* Empty string, return FALSE */
14806 		jx9_result_bool(pCtx, 0);
14807 		return JX9_OK;
14808 	}
14809 	/* Perform the requested operation */
14810 	for(;;){
14811 		if( zIn >= zEnd ){
14812 			/* If we reach the end of the string, then the test succeeded. */
14813 			jx9_result_bool(pCtx, 1);
14814 			return JX9_OK;
14815 		}
14816 		if( !SyisAlpha(zIn[0]) ){
14817 			break;
14818 		}
14819 		/* Point to the next character */
14820 		zIn++;
14821 	}
14822 	/* The test failed, return FALSE */
14823 	jx9_result_bool(pCtx, 0);
14824 	return JX9_OK;
14825 }
14826 /*
14827  * bool ctype_cntrl(string $text)
14828  *  Checks if all of the characters in the provided string, text, are control characters.
14829  * Parameters
14830  *  $text
14831  *   The tested string.
14832  * Return
14833  *  TRUE if every character in text is a control characters, FALSE otherwise.
14834  */
jx9Builtin_ctype_cntrl(jx9_context * pCtx,int nArg,jx9_value ** apArg)14835 static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
14836 {
14837 	const unsigned char *zIn, *zEnd;
14838 	int nLen;
14839 	if( nArg < 1 ){
14840 		/* Missing arguments, return FALSE */
14841 		jx9_result_bool(pCtx, 0);
14842 		return JX9_OK;
14843 	}
14844 	/* Extract the target string */
14845 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14846 	zEnd = &zIn[nLen];
14847 	if( nLen < 1 ){
14848 		/* Empty string, return FALSE */
14849 		jx9_result_bool(pCtx, 0);
14850 		return JX9_OK;
14851 	}
14852 	/* Perform the requested operation */
14853 	for(;;){
14854 		if( zIn >= zEnd ){
14855 			/* If we reach the end of the string, then the test succeeded. */
14856 			jx9_result_bool(pCtx, 1);
14857 			return JX9_OK;
14858 		}
14859 		if( zIn[0] >= 0xc0 ){
14860 			/* UTF-8 stream  */
14861 			break;
14862 		}
14863 		if( !SyisCtrl(zIn[0]) ){
14864 			break;
14865 		}
14866 		/* Point to the next character */
14867 		zIn++;
14868 	}
14869 	/* The test failed, return FALSE */
14870 	jx9_result_bool(pCtx, 0);
14871 	return JX9_OK;
14872 }
14873 /*
14874  * bool ctype_digit(string $text)
14875  *  Checks if all of the characters in the provided string, text, are numerical.
14876  * Parameters
14877  *  $text
14878  *   The tested string.
14879  * Return
14880  *  TRUE if every character in the string text is a decimal digit, FALSE otherwise.
14881  */
jx9Builtin_ctype_digit(jx9_context * pCtx,int nArg,jx9_value ** apArg)14882 static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
14883 {
14884 	const unsigned char *zIn, *zEnd;
14885 	int nLen;
14886 	if( nArg < 1 ){
14887 		/* Missing arguments, return FALSE */
14888 		jx9_result_bool(pCtx, 0);
14889 		return JX9_OK;
14890 	}
14891 	/* Extract the target string */
14892 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14893 	zEnd = &zIn[nLen];
14894 	if( nLen < 1 ){
14895 		/* Empty string, return FALSE */
14896 		jx9_result_bool(pCtx, 0);
14897 		return JX9_OK;
14898 	}
14899 	/* Perform the requested operation */
14900 	for(;;){
14901 		if( zIn >= zEnd ){
14902 			/* If we reach the end of the string, then the test succeeded. */
14903 			jx9_result_bool(pCtx, 1);
14904 			return JX9_OK;
14905 		}
14906 		if( zIn[0] >= 0xc0 ){
14907 			/* UTF-8 stream  */
14908 			break;
14909 		}
14910 		if( !SyisDigit(zIn[0]) ){
14911 			break;
14912 		}
14913 		/* Point to the next character */
14914 		zIn++;
14915 	}
14916 	/* The test failed, return FALSE */
14917 	jx9_result_bool(pCtx, 0);
14918 	return JX9_OK;
14919 }
14920 /*
14921  * bool ctype_xdigit(string $text)
14922  *  Check for character(s) representing a hexadecimal digit.
14923  * Parameters
14924  *  $text
14925  *   The tested string.
14926  * Return
14927  *  Returns TRUE if every character in text is a hexadecimal 'digit', that is
14928  * a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
14929  */
jx9Builtin_ctype_xdigit(jx9_context * pCtx,int nArg,jx9_value ** apArg)14930 static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
14931 {
14932 	const unsigned char *zIn, *zEnd;
14933 	int nLen;
14934 	if( nArg < 1 ){
14935 		/* Missing arguments, return FALSE */
14936 		jx9_result_bool(pCtx, 0);
14937 		return JX9_OK;
14938 	}
14939 	/* Extract the target string */
14940 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14941 	zEnd = &zIn[nLen];
14942 	if( nLen < 1 ){
14943 		/* Empty string, return FALSE */
14944 		jx9_result_bool(pCtx, 0);
14945 		return JX9_OK;
14946 	}
14947 	/* Perform the requested operation */
14948 	for(;;){
14949 		if( zIn >= zEnd ){
14950 			/* If we reach the end of the string, then the test succeeded. */
14951 			jx9_result_bool(pCtx, 1);
14952 			return JX9_OK;
14953 		}
14954 		if( zIn[0] >= 0xc0 ){
14955 			/* UTF-8 stream  */
14956 			break;
14957 		}
14958 		if( !SyisHex(zIn[0]) ){
14959 			break;
14960 		}
14961 		/* Point to the next character */
14962 		zIn++;
14963 	}
14964 	/* The test failed, return FALSE */
14965 	jx9_result_bool(pCtx, 0);
14966 	return JX9_OK;
14967 }
14968 /*
14969  * bool ctype_graph(string $text)
14970  *  Checks if all of the characters in the provided string, text, creates visible output.
14971  * Parameters
14972  *  $text
14973  *   The tested string.
14974  * Return
14975  *  Returns TRUE if every character in text is printable and actually creates visible output
14976  * (no white space), FALSE otherwise.
14977  */
jx9Builtin_ctype_graph(jx9_context * pCtx,int nArg,jx9_value ** apArg)14978 static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
14979 {
14980 	const unsigned char *zIn, *zEnd;
14981 	int nLen;
14982 	if( nArg < 1 ){
14983 		/* Missing arguments, return FALSE */
14984 		jx9_result_bool(pCtx, 0);
14985 		return JX9_OK;
14986 	}
14987 	/* Extract the target string */
14988 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
14989 	zEnd = &zIn[nLen];
14990 	if( nLen < 1 ){
14991 		/* Empty string, return FALSE */
14992 		jx9_result_bool(pCtx, 0);
14993 		return JX9_OK;
14994 	}
14995 	/* Perform the requested operation */
14996 	for(;;){
14997 		if( zIn >= zEnd ){
14998 			/* If we reach the end of the string, then the test succeeded. */
14999 			jx9_result_bool(pCtx, 1);
15000 			return JX9_OK;
15001 		}
15002 		if( zIn[0] >= 0xc0 ){
15003 			/* UTF-8 stream  */
15004 			break;
15005 		}
15006 		if( !SyisGraph(zIn[0]) ){
15007 			break;
15008 		}
15009 		/* Point to the next character */
15010 		zIn++;
15011 	}
15012 	/* The test failed, return FALSE */
15013 	jx9_result_bool(pCtx, 0);
15014 	return JX9_OK;
15015 }
15016 /*
15017  * bool ctype_print(string $text)
15018  *  Checks if all of the characters in the provided string, text, are printable.
15019  * Parameters
15020  *  $text
15021  *   The tested string.
15022  * Return
15023  *  Returns TRUE if every character in text will actually create output (including blanks).
15024  *  Returns FALSE if text contains control characters or characters that do not have any output
15025  *  or control function at all.
15026  */
jx9Builtin_ctype_print(jx9_context * pCtx,int nArg,jx9_value ** apArg)15027 static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
15028 {
15029 	const unsigned char *zIn, *zEnd;
15030 	int nLen;
15031 	if( nArg < 1 ){
15032 		/* Missing arguments, return FALSE */
15033 		jx9_result_bool(pCtx, 0);
15034 		return JX9_OK;
15035 	}
15036 	/* Extract the target string */
15037 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15038 	zEnd = &zIn[nLen];
15039 	if( nLen < 1 ){
15040 		/* Empty string, return FALSE */
15041 		jx9_result_bool(pCtx, 0);
15042 		return JX9_OK;
15043 	}
15044 	/* Perform the requested operation */
15045 	for(;;){
15046 		if( zIn >= zEnd ){
15047 			/* If we reach the end of the string, then the test succeeded. */
15048 			jx9_result_bool(pCtx, 1);
15049 			return JX9_OK;
15050 		}
15051 		if( zIn[0] >= 0xc0 ){
15052 			/* UTF-8 stream  */
15053 			break;
15054 		}
15055 		if( !SyisPrint(zIn[0]) ){
15056 			break;
15057 		}
15058 		/* Point to the next character */
15059 		zIn++;
15060 	}
15061 	/* The test failed, return FALSE */
15062 	jx9_result_bool(pCtx, 0);
15063 	return JX9_OK;
15064 }
15065 /*
15066  * bool ctype_punct(string $text)
15067  *  Checks if all of the characters in the provided string, text, are punctuation character.
15068  * Parameters
15069  *  $text
15070  *   The tested string.
15071  * Return
15072  *  Returns TRUE if every character in text is printable, but neither letter
15073  *  digit or blank, FALSE otherwise.
15074  */
jx9Builtin_ctype_punct(jx9_context * pCtx,int nArg,jx9_value ** apArg)15075 static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
15076 {
15077 	const unsigned char *zIn, *zEnd;
15078 	int nLen;
15079 	if( nArg < 1 ){
15080 		/* Missing arguments, return FALSE */
15081 		jx9_result_bool(pCtx, 0);
15082 		return JX9_OK;
15083 	}
15084 	/* Extract the target string */
15085 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15086 	zEnd = &zIn[nLen];
15087 	if( nLen < 1 ){
15088 		/* Empty string, return FALSE */
15089 		jx9_result_bool(pCtx, 0);
15090 		return JX9_OK;
15091 	}
15092 	/* Perform the requested operation */
15093 	for(;;){
15094 		if( zIn >= zEnd ){
15095 			/* If we reach the end of the string, then the test succeeded. */
15096 			jx9_result_bool(pCtx, 1);
15097 			return JX9_OK;
15098 		}
15099 		if( zIn[0] >= 0xc0 ){
15100 			/* UTF-8 stream  */
15101 			break;
15102 		}
15103 		if( !SyisPunct(zIn[0]) ){
15104 			break;
15105 		}
15106 		/* Point to the next character */
15107 		zIn++;
15108 	}
15109 	/* The test failed, return FALSE */
15110 	jx9_result_bool(pCtx, 0);
15111 	return JX9_OK;
15112 }
15113 /*
15114  * bool ctype_space(string $text)
15115  *  Checks if all of the characters in the provided string, text, creates whitespace.
15116  * Parameters
15117  *  $text
15118  *   The tested string.
15119  * Return
15120  *  Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
15121  *  Besides the blank character this also includes tab, vertical tab, line feed, carriage return
15122  *  and form feed characters.
15123  */
jx9Builtin_ctype_space(jx9_context * pCtx,int nArg,jx9_value ** apArg)15124 static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
15125 {
15126 	const unsigned char *zIn, *zEnd;
15127 	int nLen;
15128 	if( nArg < 1 ){
15129 		/* Missing arguments, return FALSE */
15130 		jx9_result_bool(pCtx, 0);
15131 		return JX9_OK;
15132 	}
15133 	/* Extract the target string */
15134 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15135 	zEnd = &zIn[nLen];
15136 	if( nLen < 1 ){
15137 		/* Empty string, return FALSE */
15138 		jx9_result_bool(pCtx, 0);
15139 		return JX9_OK;
15140 	}
15141 	/* Perform the requested operation */
15142 	for(;;){
15143 		if( zIn >= zEnd ){
15144 			/* If we reach the end of the string, then the test succeeded. */
15145 			jx9_result_bool(pCtx, 1);
15146 			return JX9_OK;
15147 		}
15148 		if( zIn[0] >= 0xc0 ){
15149 			/* UTF-8 stream  */
15150 			break;
15151 		}
15152 		if( !SyisSpace(zIn[0]) ){
15153 			break;
15154 		}
15155 		/* Point to the next character */
15156 		zIn++;
15157 	}
15158 	/* The test failed, return FALSE */
15159 	jx9_result_bool(pCtx, 0);
15160 	return JX9_OK;
15161 }
15162 /*
15163  * bool ctype_lower(string $text)
15164  *  Checks if all of the characters in the provided string, text, are lowercase letters.
15165  * Parameters
15166  *  $text
15167  *   The tested string.
15168  * Return
15169  *  Returns TRUE if every character in text is a lowercase letter in the current locale.
15170  */
jx9Builtin_ctype_lower(jx9_context * pCtx,int nArg,jx9_value ** apArg)15171 static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
15172 {
15173 	const unsigned char *zIn, *zEnd;
15174 	int nLen;
15175 	if( nArg < 1 ){
15176 		/* Missing arguments, return FALSE */
15177 		jx9_result_bool(pCtx, 0);
15178 		return JX9_OK;
15179 	}
15180 	/* Extract the target string */
15181 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15182 	zEnd = &zIn[nLen];
15183 	if( nLen < 1 ){
15184 		/* Empty string, return FALSE */
15185 		jx9_result_bool(pCtx, 0);
15186 		return JX9_OK;
15187 	}
15188 	/* Perform the requested operation */
15189 	for(;;){
15190 		if( zIn >= zEnd ){
15191 			/* If we reach the end of the string, then the test succeeded. */
15192 			jx9_result_bool(pCtx, 1);
15193 			return JX9_OK;
15194 		}
15195 		if( !SyisLower(zIn[0]) ){
15196 			break;
15197 		}
15198 		/* Point to the next character */
15199 		zIn++;
15200 	}
15201 	/* The test failed, return FALSE */
15202 	jx9_result_bool(pCtx, 0);
15203 	return JX9_OK;
15204 }
15205 /*
15206  * bool ctype_upper(string $text)
15207  *  Checks if all of the characters in the provided string, text, are uppercase letters.
15208  * Parameters
15209  *  $text
15210  *   The tested string.
15211  * Return
15212  *  Returns TRUE if every character in text is a uppercase letter in the current locale.
15213  */
jx9Builtin_ctype_upper(jx9_context * pCtx,int nArg,jx9_value ** apArg)15214 static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
15215 {
15216 	const unsigned char *zIn, *zEnd;
15217 	int nLen;
15218 	if( nArg < 1 ){
15219 		/* Missing arguments, return FALSE */
15220 		jx9_result_bool(pCtx, 0);
15221 		return JX9_OK;
15222 	}
15223 	/* Extract the target string */
15224 	zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
15225 	zEnd = &zIn[nLen];
15226 	if( nLen < 1 ){
15227 		/* Empty string, return FALSE */
15228 		jx9_result_bool(pCtx, 0);
15229 		return JX9_OK;
15230 	}
15231 	/* Perform the requested operation */
15232 	for(;;){
15233 		if( zIn >= zEnd ){
15234 			/* If we reach the end of the string, then the test succeeded. */
15235 			jx9_result_bool(pCtx, 1);
15236 			return JX9_OK;
15237 		}
15238 		if( !SyisUpper(zIn[0]) ){
15239 			break;
15240 		}
15241 		/* Point to the next character */
15242 		zIn++;
15243 	}
15244 	/* The test failed, return FALSE */
15245 	jx9_result_bool(pCtx, 0);
15246 	return JX9_OK;
15247 }
15248 /*
15249  * Date/Time functions
15250  * Authors:
15251  *    Symisc Systems, devel@symisc.net.
15252  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
15253  * Status:
15254  *    Devel.
15255  */
15256 #include <time.h>
15257 #ifdef __WINNT__
15258 /* GetSystemTime() */
15259 #include <Windows.h>
15260 #ifdef _WIN32_WCE
15261 /*
15262 ** WindowsCE does not have a localtime() function.  So create a
15263 ** substitute.
15264 ** Taken from the SQLite3 source tree.
15265 ** Status: Public domain
15266 */
localtime(const time_t * t)15267 struct tm *__cdecl localtime(const time_t *t)
15268 {
15269   static struct tm y;
15270   FILETIME uTm, lTm;
15271   SYSTEMTIME pTm;
15272   jx9_int64 t64;
15273   t64 = *t;
15274   t64 = (t64 + 11644473600)*10000000;
15275   uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
15276   uTm.dwHighDateTime= (DWORD)(t64 >> 32);
15277   FileTimeToLocalFileTime(&uTm, &lTm);
15278   FileTimeToSystemTime(&lTm, &pTm);
15279   y.tm_year = pTm.wYear - 1900;
15280   y.tm_mon = pTm.wMonth - 1;
15281   y.tm_wday = pTm.wDayOfWeek;
15282   y.tm_mday = pTm.wDay;
15283   y.tm_hour = pTm.wHour;
15284   y.tm_min = pTm.wMinute;
15285   y.tm_sec = pTm.wSecond;
15286   return &y;
15287 }
15288 #endif /*_WIN32_WCE */
15289 #elif defined(__UNIXES__)
15290 #include <sys/time.h>
15291 #endif /* __WINNT__*/
15292  /*
15293   * int64 time(void)
15294   *  Current Unix timestamp
15295   * Parameters
15296   *  None.
15297   * Return
15298   *  Returns the current time measured in the number of seconds
15299   *  since the Unix Epoch (January 1 1970 00:00:00 GMT).
15300   */
jx9Builtin_time(jx9_context * pCtx,int nArg,jx9_value ** apArg)15301 static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
15302 {
15303 	time_t tt;
15304 	SXUNUSED(nArg); /* cc warning */
15305 	SXUNUSED(apArg);
15306 	/* Extract the current time */
15307 	time(&tt);
15308 	/* Return as 64-bit integer */
15309 	jx9_result_int64(pCtx, (jx9_int64)tt);
15310 	return  JX9_OK;
15311 }
15312 /*
15313   * string/float microtime([ bool $get_as_float = false ])
15314   *  microtime() returns the current Unix timestamp with microseconds.
15315   * Parameters
15316   *  $get_as_float
15317   *   If used and set to TRUE, microtime() will return a float instead of a string
15318   *   as described in the return values section below.
15319   * Return
15320   *  By default, microtime() returns a string in the form "msec sec", where sec
15321   *  is the current time measured in the number of seconds since the Unix
15322   *  epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
15323   *  that have elapsed since sec expressed in seconds.
15324   *  If get_as_float is set to TRUE, then microtime() returns a float, which represents
15325   *  the current time in seconds since the Unix epoch accurate to the nearest microsecond.
15326   */
jx9Builtin_microtime(jx9_context * pCtx,int nArg,jx9_value ** apArg)15327 static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
15328 {
15329 	int bFloat = 0;
15330 	sytime sTime;
15331 #if defined(__UNIXES__)
15332 	struct timeval tv;
15333 	gettimeofday(&tv, 0);
15334 	sTime.tm_sec  = (long)tv.tv_sec;
15335 	sTime.tm_usec = (long)tv.tv_usec;
15336 #else
15337 	time_t tt;
15338 	time(&tt);
15339 	sTime.tm_sec  = (long)tt;
15340 	sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
15341 #endif /* __UNIXES__ */
15342 	if( nArg > 0 ){
15343 		bFloat = jx9_value_to_bool(apArg[0]);
15344 	}
15345 	if( bFloat ){
15346 		/* Return as float */
15347 		jx9_result_double(pCtx, (double)sTime.tm_sec);
15348 	}else{
15349 		/* Return as string */
15350 		jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
15351 	}
15352 	return JX9_OK;
15353 }
15354 /*
15355  * array getdate ([ int $timestamp = time() ])
15356  *  Get date/time information.
15357  * Parameter
15358  *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
15359  *     that defaults to the current local time if a timestamp is not given.
15360  *     In other words, it defaults to the value of time().
15361  * Returns
15362  *  Returns an associative array of information related to the timestamp.
15363  *  Elements from the returned associative array are as follows:
15364  *   KEY                                                         VALUE
15365  * ---------                                                    -------
15366  * "seconds" 	Numeric representation of seconds 	            0 to 59
15367  * "minutes" 	Numeric representation of minutes 	            0 to 59
15368  * "hours" 	    Numeric representation of hours 	            0 to 23
15369  * "mday" 	    Numeric representation of the day of the month 	1 to 31
15370  * "wday" 	    Numeric representation of the day of the week 	0 (for Sunday) through 6 (for Saturday)
15371  * "mon" 	    Numeric representation of a month 	            1 through 12
15372  * "year" 	    A full numeric representation of a year,        4 digits 	Examples: 1999 or 2003
15373  * "yday" 	    Numeric representation of the day of the year   0 through 365
15374  * "weekday" 	A full textual representation of the day of the week 	Sunday through Saturday
15375  * "month" 	    A full textual representation of a month, such as January or March 	January through December
15376  * 0 	        Seconds since the Unix Epoch, similar to the values returned by time() and used by date().
15377  * NOTE:
15378  *   NULL is returned on failure.
15379  */
jx9Builtin_getdate(jx9_context * pCtx,int nArg,jx9_value ** apArg)15380 static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
15381 {
15382 	jx9_value *pValue, *pArray;
15383 	Sytm sTm;
15384 	if( nArg < 1 ){
15385 #ifdef __WINNT__
15386 		SYSTEMTIME sOS;
15387 		GetSystemTime(&sOS);
15388 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
15389 #else
15390 		struct tm *pTm;
15391 		time_t t;
15392 		time(&t);
15393 		pTm = localtime(&t);
15394 		STRUCT_TM_TO_SYTM(pTm, &sTm);
15395 #endif
15396 	}else{
15397 		/* Use the given timestamp */
15398 		time_t t;
15399 		struct tm *pTm;
15400 #ifdef __WINNT__
15401 #ifdef _MSC_VER
15402 #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
15403 #pragma warning(disable:4996) /* _CRT_SECURE...*/
15404 #endif
15405 #endif
15406 #endif
15407 		if( jx9_value_is_int(apArg[0]) ){
15408 			t = (time_t)jx9_value_to_int64(apArg[0]);
15409 			pTm = localtime(&t);
15410 			if( pTm == 0 ){
15411 				time(&t);
15412 			}
15413 		}else{
15414 			time(&t);
15415 		}
15416 		pTm = localtime(&t);
15417 		STRUCT_TM_TO_SYTM(pTm, &sTm);
15418 	}
15419 	/* Element value */
15420 	pValue = jx9_context_new_scalar(pCtx);
15421 	if( pValue == 0 ){
15422 		/* Return NULL */
15423 		jx9_result_null(pCtx);
15424 		return JX9_OK;
15425 	}
15426 	/* Create a new array */
15427 	pArray = jx9_context_new_array(pCtx);
15428 	if( pArray == 0 ){
15429 		/* Return NULL */
15430 		jx9_result_null(pCtx);
15431 		return JX9_OK;
15432 	}
15433 	/* Fill the array */
15434 	/* Seconds */
15435 	jx9_value_int(pValue, sTm.tm_sec);
15436 	jx9_array_add_strkey_elem(pArray, "seconds", pValue);
15437 	/* Minutes */
15438 	jx9_value_int(pValue, sTm.tm_min);
15439 	jx9_array_add_strkey_elem(pArray, "minutes", pValue);
15440 	/* Hours */
15441 	jx9_value_int(pValue, sTm.tm_hour);
15442 	jx9_array_add_strkey_elem(pArray, "hours", pValue);
15443 	/* mday */
15444 	jx9_value_int(pValue, sTm.tm_mday);
15445 	jx9_array_add_strkey_elem(pArray, "mday", pValue);
15446 	/* wday */
15447 	jx9_value_int(pValue, sTm.tm_wday);
15448 	jx9_array_add_strkey_elem(pArray, "wday", pValue);
15449 	/* mon */
15450 	jx9_value_int(pValue, sTm.tm_mon+1);
15451 	jx9_array_add_strkey_elem(pArray, "mon", pValue);
15452 	/* year */
15453 	jx9_value_int(pValue, sTm.tm_year);
15454 	jx9_array_add_strkey_elem(pArray, "year", pValue);
15455 	/* yday */
15456 	jx9_value_int(pValue, sTm.tm_yday);
15457 	jx9_array_add_strkey_elem(pArray, "yday", pValue);
15458 	/* Weekday */
15459 	jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
15460 	jx9_array_add_strkey_elem(pArray, "weekday", pValue);
15461 	/* Month */
15462 	jx9_value_reset_string_cursor(pValue);
15463 	jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
15464 	jx9_array_add_strkey_elem(pArray, "month", pValue);
15465 	/* Seconds since the epoch */
15466 	jx9_value_int64(pValue, (jx9_int64)time(0));
15467 	jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
15468 	/* Return the freshly created array */
15469 	jx9_result_value(pCtx, pArray);
15470 	return JX9_OK;
15471 }
15472 /*
15473  * mixed gettimeofday([ bool $return_float = false ] )
15474  *   Returns an associative array containing the data returned from the system call.
15475  * Parameters
15476  *  $return_float
15477  *   When set to TRUE, a float instead of an array is returned.
15478  * Return
15479  *   By default an array is returned. If return_float is set, then
15480  *   a float is returned.
15481  */
jx9Builtin_gettimeofday(jx9_context * pCtx,int nArg,jx9_value ** apArg)15482 static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
15483 {
15484 	int bFloat = 0;
15485 	sytime sTime;
15486 #if defined(__UNIXES__)
15487 	struct timeval tv;
15488 	gettimeofday(&tv, 0);
15489 	sTime.tm_sec  = (long)tv.tv_sec;
15490 	sTime.tm_usec = (long)tv.tv_usec;
15491 #else
15492 	time_t tt;
15493 	time(&tt);
15494 	sTime.tm_sec  = (long)tt;
15495 	sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
15496 #endif /* __UNIXES__ */
15497 	if( nArg > 0 ){
15498 		bFloat = jx9_value_to_bool(apArg[0]);
15499 	}
15500 	if( bFloat ){
15501 		/* Return as float */
15502 		jx9_result_double(pCtx, (double)sTime.tm_sec);
15503 	}else{
15504 		/* Return an associative array */
15505 		jx9_value *pValue, *pArray;
15506 		/* Create a new array */
15507 		pArray = jx9_context_new_array(pCtx);
15508 		/* Element value */
15509 		pValue = jx9_context_new_scalar(pCtx);
15510 		if( pValue == 0 || pArray == 0 ){
15511 			/* Return NULL */
15512 			jx9_result_null(pCtx);
15513 			return JX9_OK;
15514 		}
15515 		/* Fill the array */
15516 		/* sec */
15517 		jx9_value_int64(pValue, sTime.tm_sec);
15518 		jx9_array_add_strkey_elem(pArray, "sec", pValue);
15519 		/* usec */
15520 		jx9_value_int64(pValue, sTime.tm_usec);
15521 		jx9_array_add_strkey_elem(pArray, "usec", pValue);
15522 		/* Return the array */
15523 		jx9_result_value(pCtx, pArray);
15524 	}
15525 	return JX9_OK;
15526 }
15527 /* Check if the given year is leap or not */
15528 #define IS_LEAP_YEAR(YEAR)	(YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
15529 /* ISO-8601 numeric representation of the day of the week */
15530 static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
15531 /*
15532  * Format a given date string.
15533  * Supported format: (Taken from JX9 online docs)
15534  * character 	Description
15535  * d          Day of the month
15536  * D          A textual representation of a days
15537  * j          Day of the month without leading zeros
15538  * l          A full textual representation of the day of the week
15539  * N          ISO-8601 numeric representation of the day of the week
15540  * w          Numeric representation of the day of the week
15541  * z          The day of the year (starting from 0)
15542  * F          A full textual representation of a month, such as January or March
15543  * m          Numeric representation of a month, with leading zeros 	01 through 12
15544  * M          A short textual representation of a month, three letters 	Jan through Dec
15545  * n          Numeric representation of a month, without leading zeros 	1 through 12
15546  * t          Number of days in the given month 	28 through 31
15547  * L          Whether it's a leap year 	1 if it is a leap year, 0 otherwise.
15548  * o          ISO-8601 year number. This has the same value as Y, except that if the ISO week number
15549  *            (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
15550  * Y          A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
15551  * y          A two digit representation of a year 	Examples: 99 or 03
15552  * a          Lowercase Ante meridiem and Post meridiem 	am or pm
15553  * A          Uppercase Ante meridiem and Post meridiem 	AM or PM
15554  * g          12-hour format of an hour without leading zeros 	1 through 12
15555  * G          24-hour format of an hour without leading zeros 	0 through 23
15556  * h          12-hour format of an hour with leading zeros 	01 through 12
15557  * H          24-hour format of an hour with leading zeros 	00 through 23
15558  * i          Minutes with leading zeros 	00 to 59
15559  * s          Seconds, with leading zeros 	00 through 59
15560  * u          Microseconds Example: 654321
15561  * e          Timezone identifier 	Examples: UTC, GMT, Atlantic/Azores
15562  * I          (capital i) Whether or not the date is in daylight saving time 	1 if Daylight Saving Time, 0 otherwise.
15563  * r          RFC 2822 formatted date 	Example: Thu, 21 Dec 2000 16:01:07 +0200
15564  * U          Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
15565  * S          English ordinal suffix for the day of the month, 2 characters
15566  * O          Difference to Greenwich time (GMT) in hours
15567  * Z          Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
15568  *            east of UTC is always positive.
15569  * c         ISO 8601 date
15570  */
DateFormat(jx9_context * pCtx,const char * zIn,int nLen,Sytm * pTm)15571 static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
15572 {
15573 	const char *zEnd = &zIn[nLen];
15574 	const char *zCur;
15575 	/* Start the format process */
15576 	for(;;){
15577 		if( zIn >= zEnd ){
15578 			/* No more input to process */
15579 			break;
15580 		}
15581 		switch(zIn[0]){
15582 		case 'd':
15583 			/* Day of the month, 2 digits with leading zeros */
15584 			jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
15585 			break;
15586 		case 'D':
15587 			/*A textual representation of a day, three letters*/
15588 			zCur = SyTimeGetDay(pTm->tm_wday);
15589 			jx9_result_string(pCtx, zCur, 3);
15590 			break;
15591 		case 'j':
15592 			/*	Day of the month without leading zeros */
15593 			jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
15594 			break;
15595 		case 'l':
15596 			/* A full textual representation of the day of the week */
15597 			zCur = SyTimeGetDay(pTm->tm_wday);
15598 			jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
15599 			break;
15600 		case 'N':{
15601 			/* ISO-8601 numeric representation of the day of the week */
15602 			jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
15603 			break;
15604 				 }
15605 		case 'w':
15606 			/*Numeric representation of the day of the week*/
15607 			jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
15608 			break;
15609 		case 'z':
15610 			/*The day of the year*/
15611 			jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
15612 			break;
15613 		case 'F':
15614 			/*A full textual representation of a month, such as January or March*/
15615 			zCur = SyTimeGetMonth(pTm->tm_mon);
15616 			jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
15617 			break;
15618 		case 'm':
15619 			/*Numeric representation of a month, with leading zeros*/
15620 			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
15621 			break;
15622 		case 'M':
15623 			/*A short textual representation of a month, three letters*/
15624 			zCur = SyTimeGetMonth(pTm->tm_mon);
15625 			jx9_result_string(pCtx, zCur, 3);
15626 			break;
15627 		case 'n':
15628 			/*Numeric representation of a month, without leading zeros*/
15629 			jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
15630 			break;
15631 		case 't':{
15632 			static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
15633 			int nDays = aMonDays[pTm->tm_mon % 12 ];
15634 			if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
15635 				nDays = 28;
15636 			}
15637 			/*Number of days in the given month*/
15638 			jx9_result_string_format(pCtx, "%d", nDays);
15639 			break;
15640 				 }
15641 		case 'L':{
15642 			int isLeap = IS_LEAP_YEAR(pTm->tm_year);
15643 			/* Whether it's a leap year */
15644 			jx9_result_string_format(pCtx, "%d", isLeap);
15645 			break;
15646 				 }
15647 		case 'o':
15648 			/* ISO-8601 year number.*/
15649 			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15650 			break;
15651 		case 'Y':
15652 			/*	A full numeric representation of a year, 4 digits */
15653 			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15654 			break;
15655 		case 'y':
15656 			/*A two digit representation of a year*/
15657 			jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
15658 			break;
15659 		case 'a':
15660 			/*	Lowercase Ante meridiem and Post meridiem */
15661 			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
15662 			break;
15663 		case 'A':
15664 			/*	Uppercase Ante meridiem and Post meridiem */
15665 			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
15666 			break;
15667 		case 'g':
15668 			/*	12-hour format of an hour without leading zeros*/
15669 			jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
15670 			break;
15671 		case 'G':
15672 			/* 24-hour format of an hour without leading zeros */
15673 			jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
15674 			break;
15675 		case 'h':
15676 			/* 12-hour format of an hour with leading zeros */
15677 			jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
15678 			break;
15679 		case 'H':
15680 			/*	24-hour format of an hour with leading zeros */
15681 			jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
15682 			break;
15683 		case 'i':
15684 			/* 	Minutes with leading zeros */
15685 			jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
15686 			break;
15687 		case 's':
15688 			/* 	second with leading zeros */
15689 			jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
15690 			break;
15691 		case 'u':
15692 			/* 	Microseconds */
15693 			jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
15694 			break;
15695 		case 'S':{
15696 			/* English ordinal suffix for the day of the month, 2 characters */
15697 			static const char zSuffix[] = "thstndrdthththththth";
15698 			int v = pTm->tm_mday;
15699 			jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
15700 			break;
15701 				 }
15702 		case 'e':
15703 			/* 	Timezone identifier */
15704 			zCur = pTm->tm_zone;
15705 			if( zCur == 0 ){
15706 				/* Assume GMT */
15707 				zCur = "GMT";
15708 			}
15709 			jx9_result_string(pCtx, zCur, -1);
15710 			break;
15711 		case 'I':
15712 			/* Whether or not the date is in daylight saving time */
15713 #ifdef __WINNT__
15714 #ifdef _MSC_VER
15715 #ifndef _WIN32_WCE
15716 			_get_daylight(&pTm->tm_isdst);
15717 #endif
15718 #endif
15719 #endif
15720 			jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
15721 			break;
15722 		case 'r':
15723 			/* RFC 2822 formatted date 	Example: Thu, 21 Dec 2000 16:01:07 */
15724 			jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d",
15725 				SyTimeGetDay(pTm->tm_wday),
15726 				pTm->tm_mday,
15727 				SyTimeGetMonth(pTm->tm_mon),
15728 				pTm->tm_year,
15729 				pTm->tm_hour,
15730 				pTm->tm_min,
15731 				pTm->tm_sec
15732 				);
15733 			break;
15734 		case 'U':{
15735 			time_t tt;
15736 			/* Seconds since the Unix Epoch */
15737 			time(&tt);
15738 			jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
15739 			break;
15740 				 }
15741 		case 'O':
15742 		case 'P':
15743 			/* Difference to Greenwich time (GMT) in hours */
15744 			jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
15745 			break;
15746 		case 'Z':
15747 			/* Timezone offset in seconds. The offset for timezones west of UTC
15748 			 * is always negative, and for those east of UTC is always positive.
15749 			 */
15750 			jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
15751 			break;
15752 		case 'c':
15753 			/* 	ISO 8601 date */
15754 			jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d",
15755 				pTm->tm_year,
15756 				pTm->tm_mon+1,
15757 				pTm->tm_mday,
15758 				pTm->tm_hour,
15759 				pTm->tm_min,
15760 				pTm->tm_sec,
15761 				pTm->tm_gmtoff
15762 				);
15763 			break;
15764 		case '\\':
15765 			zIn++;
15766 			/* Expand verbatim */
15767 			if( zIn < zEnd ){
15768 				jx9_result_string(pCtx, zIn, (int)sizeof(char));
15769 			}
15770 			break;
15771 		default:
15772 			/* Unknown format specifer, expand verbatim */
15773 			jx9_result_string(pCtx, zIn, (int)sizeof(char));
15774 			break;
15775 		}
15776 		/* Point to the next character */
15777 		zIn++;
15778 	}
15779 	return SXRET_OK;
15780 }
15781 /*
15782  * JX9 implementation of the strftime() function.
15783  * The following formats are supported:
15784  * %a 	An abbreviated textual representation of the day
15785  * %A 	A full textual representation of the day
15786  * %d 	Two-digit day of the month (with leading zeros)
15787  * %e 	Day of the month, with a space preceding single digits.
15788  * %j 	Day of the year, 3 digits with leading zeros
15789  * %u 	ISO-8601 numeric representation of the day of the week 	1 (for Monday) though 7 (for Sunday)
15790  * %w 	Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
15791  * %U 	Week number of the given year, starting with the first Sunday as the first week
15792  * %V 	ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
15793  *   4 weekdays, with Monday being the start of the week.
15794  * %W 	A numeric representation of the week of the year
15795  * %b 	Abbreviated month name, based on the locale
15796  * %B 	Full month name, based on the locale
15797  * %h 	Abbreviated month name, based on the locale (an alias of %b)
15798  * %m 	Two digit representation of the month
15799  * %C 	Two digit representation of the century (year divided by 100, truncated to an integer)
15800  * %g 	Two digit representation of the year going by ISO-8601:1988 standards (see %V)
15801  * %G 	The full four-digit version of %g
15802  * %y 	Two digit representation of the year
15803  * %Y 	Four digit representation for the year
15804  * %H 	Two digit representation of the hour in 24-hour format
15805  * %I 	Two digit representation of the hour in 12-hour format
15806  * %l (lower-case 'L') 	Hour in 12-hour format, with a space preceeding single digits
15807  * %M 	Two digit representation of the minute
15808  * %p 	UPPER-CASE 'AM' or 'PM' based on the given time
15809  * %P 	lower-case 'am' or 'pm' based on the given time
15810  * %r 	Same as "%I:%M:%S %p"
15811  * %R 	Same as "%H:%M"
15812  * %S 	Two digit representation of the second
15813  * %T 	Same as "%H:%M:%S"
15814  * %X 	Preferred time representation based on locale, without the date
15815  * %z 	Either the time zone offset from UTC or the abbreviation
15816  * %Z 	The time zone offset/abbreviation option NOT given by %z
15817  * %c 	Preferred date and time stamp based on local
15818  * %D 	Same as "%m/%d/%y"
15819  * %F 	Same as "%Y-%m-%d"
15820  * %s 	Unix Epoch Time timestamp (same as the time() function)
15821  * %x 	Preferred date representation based on locale, without the time
15822  * %n 	A newline character ("\n")
15823  * %t 	A Tab character ("\t")
15824  * %% 	A literal percentage character ("%")
15825  */
jx9Strftime(jx9_context * pCtx,const char * zIn,int nLen,Sytm * pTm)15826 static int jx9Strftime(
15827 	jx9_context *pCtx,  /* Call context */
15828 	const char *zIn,    /* Input string */
15829 	int nLen,           /* Input length */
15830 	Sytm *pTm           /* Parse of the given time */
15831 	)
15832 {
15833 	const char *zCur, *zEnd = &zIn[nLen];
15834 	int c;
15835 	/* Start the format process */
15836 	for(;;){
15837 		zCur = zIn;
15838 		while(zIn < zEnd && zIn[0] != '%' ){
15839 			zIn++;
15840 		}
15841 		if( zIn > zCur ){
15842 			/* Consume input verbatim */
15843 			jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
15844 		}
15845 		zIn++; /* Jump the percent sign */
15846 		if( zIn >= zEnd ){
15847 			/* No more input to process */
15848 			break;
15849 		}
15850 		c = zIn[0];
15851 		/* Act according to the current specifer */
15852 		switch(c){
15853 		case '%':
15854 			/* A literal percentage character ("%") */
15855 			jx9_result_string(pCtx, "%", (int)sizeof(char));
15856 			break;
15857 		case 't':
15858 			/* A Tab character */
15859 			jx9_result_string(pCtx, "\t", (int)sizeof(char));
15860 			break;
15861 		case 'n':
15862 			/* A newline character */
15863 			jx9_result_string(pCtx, "\n", (int)sizeof(char));
15864 			break;
15865 		case 'a':
15866 			/* An abbreviated textual representation of the day */
15867 			jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
15868 			break;
15869 		case 'A':
15870 			/* A full textual representation of the day */
15871 			jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
15872 			break;
15873 		case 'e':
15874 			/* Day of the month, 2 digits with leading space for single digit*/
15875 			jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
15876 			break;
15877 		case 'd':
15878 			/* Two-digit day of the month (with leading zeros) */
15879 			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
15880 			break;
15881 		case 'j':
15882 			/*The day of the year, 3 digits with leading zeros*/
15883 			jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
15884 			break;
15885 		case 'u':
15886 			/* ISO-8601 numeric representation of the day of the week */
15887 			jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
15888 			break;
15889 		case 'w':
15890 			/* Numeric representation of the day of the week */
15891 			jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
15892 			break;
15893 		case 'b':
15894 		case 'h':
15895 			/*A short textual representation of a month, three letters (Not based on locale)*/
15896 			jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
15897 			break;
15898 		case 'B':
15899 			/* Full month name (Not based on locale) */
15900 			jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
15901 			break;
15902 		case 'm':
15903 			/*Numeric representation of a month, with leading zeros*/
15904 			jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
15905 			break;
15906 		case 'C':
15907 			/* Two digit representation of the century */
15908 			jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
15909 			break;
15910 		case 'y':
15911 		case 'g':
15912 			/* Two digit representation of the year */
15913 			jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
15914 			break;
15915 		case 'Y':
15916 		case 'G':
15917 			/* Four digit representation of the year */
15918 			jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
15919 			break;
15920 		case 'I':
15921 			/* 12-hour format of an hour with leading zeros */
15922 			jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
15923 			break;
15924 		case 'l':
15925 			/* 12-hour format of an hour with leading space */
15926 			jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
15927 			break;
15928 		case 'H':
15929 			/* 24-hour format of an hour with leading zeros */
15930 			jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
15931 			break;
15932 		case 'M':
15933 			/* Minutes with leading zeros */
15934 			jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
15935 			break;
15936 		case 'S':
15937 			/* Seconds with leading zeros */
15938 			jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
15939 			break;
15940 		case 'z':
15941 		case 'Z':
15942 			/* 	Timezone identifier */
15943 			zCur = pTm->tm_zone;
15944 			if( zCur == 0 ){
15945 				/* Assume GMT */
15946 				zCur = "GMT";
15947 			}
15948 			jx9_result_string(pCtx, zCur, -1);
15949 			break;
15950 		case 'T':
15951 		case 'X':
15952 			/* Same as "%H:%M:%S" */
15953 			jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
15954 			break;
15955 		case 'R':
15956 			/* Same as "%H:%M" */
15957 			jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
15958 			break;
15959 		case 'P':
15960 			/*	Lowercase Ante meridiem and Post meridiem */
15961 			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
15962 			break;
15963 		case 'p':
15964 			/*	Uppercase Ante meridiem and Post meridiem */
15965 			jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
15966 			break;
15967 		case 'r':
15968 			/* Same as "%I:%M:%S %p" */
15969 			jx9_result_string_format(pCtx, "%02d:%02d:%02d %s",
15970 				1+(pTm->tm_hour%12),
15971 				pTm->tm_min,
15972 				pTm->tm_sec,
15973 				pTm->tm_hour > 12 ? "PM" : "AM"
15974 				);
15975 			break;
15976 		case 'D':
15977 		case 'x':
15978 			/* Same as "%m/%d/%y" */
15979 			jx9_result_string_format(pCtx, "%02d/%02d/%02d",
15980 				pTm->tm_mon+1,
15981 				pTm->tm_mday,
15982 				pTm->tm_year%100
15983 				);
15984 			break;
15985 		case 'F':
15986 			/* Same as "%Y-%m-%d" */
15987 			jx9_result_string_format(pCtx, "%d-%02d-%02d",
15988 				pTm->tm_year,
15989 				pTm->tm_mon+1,
15990 				pTm->tm_mday
15991 				);
15992 			break;
15993 		case 'c':
15994 			jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d",
15995 				pTm->tm_year,
15996 				pTm->tm_mon+1,
15997 				pTm->tm_mday,
15998 				pTm->tm_hour,
15999 				pTm->tm_min,
16000 				pTm->tm_sec
16001 				);
16002 			break;
16003 		case 's':{
16004 			time_t tt;
16005 			/* Seconds since the Unix Epoch */
16006 			time(&tt);
16007 			jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
16008 			break;
16009 				 }
16010 		default:
16011 			/* unknown specifer, simply ignore*/
16012 			break;
16013 		}
16014 		/* Advance the cursor */
16015 		zIn++;
16016 	}
16017 	return SXRET_OK;
16018 }
16019 /*
16020  * string date(string $format [, int $timestamp = time() ] )
16021  *  Returns a string formatted according to the given format string using
16022  *  the given integer timestamp or the current time if no timestamp is given.
16023  *  In other words, timestamp is optional and defaults to the value of time().
16024  * Parameters
16025  *  $format
16026  *   The format of the outputted date string (See code above)
16027  * $timestamp
16028  *   The optional timestamp parameter is an integer Unix timestamp
16029  *   that defaults to the current local time if a timestamp is not given.
16030  *   In other words, it defaults to the value of time().
16031  * Return
16032  *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
16033  */
jx9Builtin_date(jx9_context * pCtx,int nArg,jx9_value ** apArg)16034 static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
16035 {
16036 	const char *zFormat;
16037 	int nLen;
16038 	Sytm sTm;
16039 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16040 		/* Missing/Invalid argument, return FALSE */
16041 		jx9_result_bool(pCtx, 0);
16042 		return JX9_OK;
16043 	}
16044 	zFormat = jx9_value_to_string(apArg[0], &nLen);
16045 	if( nLen < 1 ){
16046 		/* Don't bother processing return the empty string */
16047 		jx9_result_string(pCtx, "", 0);
16048 	}
16049 	if( nArg < 2 ){
16050 #ifdef __WINNT__
16051 		SYSTEMTIME sOS;
16052 		GetSystemTime(&sOS);
16053 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16054 #else
16055 		struct tm *pTm;
16056 		time_t t;
16057 		time(&t);
16058 		pTm = localtime(&t);
16059 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16060 #endif
16061 	}else{
16062 		/* Use the given timestamp */
16063 		time_t t;
16064 		struct tm *pTm;
16065 		if( jx9_value_is_int(apArg[1]) ){
16066 			t = (time_t)jx9_value_to_int64(apArg[1]);
16067 			pTm = localtime(&t);
16068 			if( pTm == 0 ){
16069 				time(&t);
16070 			}
16071 		}else{
16072 			time(&t);
16073 		}
16074 		pTm = localtime(&t);
16075 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16076 	}
16077 	/* Format the given string */
16078 	DateFormat(pCtx, zFormat, nLen, &sTm);
16079 	return JX9_OK;
16080 }
16081 /*
16082  * string strftime(string $format [, int $timestamp = time() ] )
16083  *  Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
16084  * Parameters
16085  *  $format
16086  *   The format of the outputted date string (See code above)
16087  * $timestamp
16088  *   The optional timestamp parameter is an integer Unix timestamp
16089  *   that defaults to the current local time if a timestamp is not given.
16090  *   In other words, it defaults to the value of time().
16091  * Return
16092  * Returns a string formatted according format using the given timestamp
16093  * or the current local time if no timestamp is given.
16094  */
jx9Builtin_strftime(jx9_context * pCtx,int nArg,jx9_value ** apArg)16095 static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16096 {
16097 	const char *zFormat;
16098 	int nLen;
16099 	Sytm sTm;
16100 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16101 		/* Missing/Invalid argument, return FALSE */
16102 		jx9_result_bool(pCtx, 0);
16103 		return JX9_OK;
16104 	}
16105 	zFormat = jx9_value_to_string(apArg[0], &nLen);
16106 	if( nLen < 1 ){
16107 		/* Don't bother processing return FALSE */
16108 		jx9_result_bool(pCtx, 0);
16109 	}
16110 	if( nArg < 2 ){
16111 #ifdef __WINNT__
16112 		SYSTEMTIME sOS;
16113 		GetSystemTime(&sOS);
16114 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16115 #else
16116 		struct tm *pTm;
16117 		time_t t;
16118 		time(&t);
16119 		pTm = localtime(&t);
16120 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16121 #endif
16122 	}else{
16123 		/* Use the given timestamp */
16124 		time_t t;
16125 		struct tm *pTm;
16126 		if( jx9_value_is_int(apArg[1]) ){
16127 			t = (time_t)jx9_value_to_int64(apArg[1]);
16128 			pTm = localtime(&t);
16129 			if( pTm == 0 ){
16130 				time(&t);
16131 			}
16132 		}else{
16133 			time(&t);
16134 		}
16135 		pTm = localtime(&t);
16136 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16137 	}
16138 	/* Format the given string */
16139 	jx9Strftime(pCtx, zFormat, nLen, &sTm);
16140 	if( jx9_context_result_buf_length(pCtx) < 1 ){
16141 		/* Nothing was formatted, return FALSE */
16142 		jx9_result_bool(pCtx, 0);
16143 	}
16144 	return JX9_OK;
16145 }
16146 /*
16147  * string gmdate(string $format [, int $timestamp = time() ] )
16148  *  Identical to the date() function except that the time returned
16149  *  is Greenwich Mean Time (GMT).
16150  * Parameters
16151  *  $format
16152  *  The format of the outputted date string (See code above)
16153  *  $timestamp
16154  *   The optional timestamp parameter is an integer Unix timestamp
16155  *   that defaults to the current local time if a timestamp is not given.
16156  *   In other words, it defaults to the value of time().
16157  * Return
16158  *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
16159  */
jx9Builtin_gmdate(jx9_context * pCtx,int nArg,jx9_value ** apArg)16160 static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
16161 {
16162 	const char *zFormat;
16163 	int nLen;
16164 	Sytm sTm;
16165 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16166 		/* Missing/Invalid argument, return FALSE */
16167 		jx9_result_bool(pCtx, 0);
16168 		return JX9_OK;
16169 	}
16170 	zFormat = jx9_value_to_string(apArg[0], &nLen);
16171 	if( nLen < 1 ){
16172 		/* Don't bother processing return the empty string */
16173 		jx9_result_string(pCtx, "", 0);
16174 	}
16175 	if( nArg < 2 ){
16176 #ifdef __WINNT__
16177 		SYSTEMTIME sOS;
16178 		GetSystemTime(&sOS);
16179 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16180 #else
16181 		struct tm *pTm;
16182 		time_t t;
16183 		time(&t);
16184 		pTm = gmtime(&t);
16185 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16186 #endif
16187 	}else{
16188 		/* Use the given timestamp */
16189 		time_t t;
16190 		struct tm *pTm;
16191 		if( jx9_value_is_int(apArg[1]) ){
16192 			t = (time_t)jx9_value_to_int64(apArg[1]);
16193 			pTm = gmtime(&t);
16194 			if( pTm == 0 ){
16195 				time(&t);
16196 			}
16197 		}else{
16198 			time(&t);
16199 		}
16200 		pTm = gmtime(&t);
16201 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16202 	}
16203 	/* Format the given string */
16204 	DateFormat(pCtx, zFormat, nLen, &sTm);
16205 	return JX9_OK;
16206 }
16207 /*
16208  * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
16209  *  Return the local time.
16210  * Parameter
16211  *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
16212  *     that defaults to the current local time if a timestamp is not given.
16213  *     In other words, it defaults to the value of time().
16214  * $is_associative
16215  *   If set to FALSE or not supplied then the array is returned as a regular, numerically
16216  *   indexed array. If the argument is set to TRUE then localtime() returns an associative
16217  *   array containing all the different elements of the structure returned by the C function
16218  *   call to localtime. The names of the different keys of the associative array are as follows:
16219  *      "tm_sec" - seconds, 0 to 59
16220  *      "tm_min" - minutes, 0 to 59
16221  *      "tm_hour" - hours, 0 to 23
16222  *      "tm_mday" - day of the month, 1 to 31
16223  *      "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
16224  *      "tm_year" - years since 1900
16225  *      "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
16226  *      "tm_yday" - day of the year, 0 to 365
16227  *      "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
16228  * Returns
16229  *  An associative array of information related to the timestamp.
16230  */
jx9Builtin_localtime(jx9_context * pCtx,int nArg,jx9_value ** apArg)16231 static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16232 {
16233 	jx9_value *pValue, *pArray;
16234 	int isAssoc = 0;
16235 	Sytm sTm;
16236 	if( nArg < 1 ){
16237 #ifdef __WINNT__
16238 		SYSTEMTIME sOS;
16239 		GetSystemTime(&sOS); /* TODO(chems): GMT not local */
16240 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16241 #else
16242 		struct tm *pTm;
16243 		time_t t;
16244 		time(&t);
16245 		pTm = localtime(&t);
16246 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16247 #endif
16248 	}else{
16249 		/* Use the given timestamp */
16250 		time_t t;
16251 		struct tm *pTm;
16252 		if( jx9_value_is_int(apArg[0]) ){
16253 			t = (time_t)jx9_value_to_int64(apArg[0]);
16254 			pTm = localtime(&t);
16255 			if( pTm == 0 ){
16256 				time(&t);
16257 			}
16258 		}else{
16259 			time(&t);
16260 		}
16261 		pTm = localtime(&t);
16262 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16263 	}
16264 	/* Element value */
16265 	pValue = jx9_context_new_scalar(pCtx);
16266 	if( pValue == 0 ){
16267 		/* Return NULL */
16268 		jx9_result_null(pCtx);
16269 		return JX9_OK;
16270 	}
16271 	/* Create a new array */
16272 	pArray = jx9_context_new_array(pCtx);
16273 	if( pArray == 0 ){
16274 		/* Return NULL */
16275 		jx9_result_null(pCtx);
16276 		return JX9_OK;
16277 	}
16278 	if( nArg > 1 ){
16279 		isAssoc = jx9_value_to_bool(apArg[1]);
16280 	}
16281 	/* Fill the array */
16282 	/* Seconds */
16283 	jx9_value_int(pValue, sTm.tm_sec);
16284 	if( isAssoc ){
16285 		jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
16286 	}else{
16287 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16288 	}
16289 	/* Minutes */
16290 	jx9_value_int(pValue, sTm.tm_min);
16291 	if( isAssoc ){
16292 		jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
16293 	}else{
16294 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16295 	}
16296 	/* Hours */
16297 	jx9_value_int(pValue, sTm.tm_hour);
16298 	if( isAssoc ){
16299 		jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
16300 	}else{
16301 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16302 	}
16303 	/* mday */
16304 	jx9_value_int(pValue, sTm.tm_mday);
16305 	if( isAssoc ){
16306 		jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
16307 	}else{
16308 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16309 	}
16310 	/* mon */
16311 	jx9_value_int(pValue, sTm.tm_mon);
16312 	if( isAssoc ){
16313 		jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
16314 	}else{
16315 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16316 	}
16317 	/* year since 1900 */
16318 	jx9_value_int(pValue, sTm.tm_year-1900);
16319 	if( isAssoc ){
16320 		jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
16321 	}else{
16322 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16323 	}
16324 	/* wday */
16325 	jx9_value_int(pValue, sTm.tm_wday);
16326 	if( isAssoc ){
16327 		jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
16328 	}else{
16329 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16330 	}
16331 	/* yday */
16332 	jx9_value_int(pValue, sTm.tm_yday);
16333 	if( isAssoc ){
16334 		jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
16335 	}else{
16336 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16337 	}
16338 	/* isdst */
16339 #ifdef __WINNT__
16340 #ifdef _MSC_VER
16341 #ifndef _WIN32_WCE
16342 			_get_daylight(&sTm.tm_isdst);
16343 #endif
16344 #endif
16345 #endif
16346 	jx9_value_int(pValue, sTm.tm_isdst);
16347 	if( isAssoc ){
16348 		jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
16349 	}else{
16350 		jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
16351 	}
16352 	/* Return the array */
16353 	jx9_result_value(pCtx, pArray);
16354 	return JX9_OK;
16355 }
16356 /*
16357  * int idate(string $format [, int $timestamp = time() ])
16358  *  Returns a number formatted according to the given format string
16359  *  using the given integer timestamp or the current local time if
16360  *  no timestamp is given. In other words, timestamp is optional and defaults
16361  *  to the value of time().
16362  *  Unlike the function date(), idate() accepts just one char in the format
16363  *  parameter.
16364  * $Parameters
16365  *  Supported format
16366  *   d 	Day of the month
16367  *   h 	Hour (12 hour format)
16368  *   H 	Hour (24 hour format)
16369  *   i 	Minutes
16370  *   I (uppercase i)1 if DST is activated, 0 otherwise
16371  *   L (uppercase l) returns 1 for leap year, 0 otherwise
16372  *   m 	Month number
16373  *   s 	Seconds
16374  *   t 	Days in current month
16375  *   U 	Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
16376  *   w 	Day of the week (0 on Sunday)
16377  *   W 	ISO-8601 week number of year, weeks starting on Monday
16378  *   y 	Year (1 or 2 digits - check note below)
16379  *   Y 	Year (4 digits)
16380  *   z 	Day of the year
16381  *   Z 	Timezone offset in seconds
16382  * $timestamp
16383  *  The optional timestamp parameter is an integer Unix timestamp that defaults
16384  *  to the current local time if a timestamp is not given. In other words, it defaults
16385  *  to the value of time().
16386  * Return
16387  *  An integer.
16388  */
jx9Builtin_idate(jx9_context * pCtx,int nArg,jx9_value ** apArg)16389 static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
16390 {
16391 	const char *zFormat;
16392 	jx9_int64 iVal = 0;
16393 	int nLen;
16394 	Sytm sTm;
16395 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
16396 		/* Missing/Invalid argument, return -1 */
16397 		jx9_result_int(pCtx, -1);
16398 		return JX9_OK;
16399 	}
16400 	zFormat = jx9_value_to_string(apArg[0], &nLen);
16401 	if( nLen < 1 ){
16402 		/* Don't bother processing return -1*/
16403 		jx9_result_int(pCtx, -1);
16404 	}
16405 	if( nArg < 2 ){
16406 #ifdef __WINNT__
16407 		SYSTEMTIME sOS;
16408 		GetSystemTime(&sOS);
16409 		SYSTEMTIME_TO_SYTM(&sOS, &sTm);
16410 #else
16411 		struct tm *pTm;
16412 		time_t t;
16413 		time(&t);
16414 		pTm = localtime(&t);
16415 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16416 #endif
16417 	}else{
16418 		/* Use the given timestamp */
16419 		time_t t;
16420 		struct tm *pTm;
16421 		if( jx9_value_is_int(apArg[1]) ){
16422 			t = (time_t)jx9_value_to_int64(apArg[1]);
16423 			pTm = localtime(&t);
16424 			if( pTm == 0 ){
16425 				time(&t);
16426 			}
16427 		}else{
16428 			time(&t);
16429 		}
16430 		pTm = localtime(&t);
16431 		STRUCT_TM_TO_SYTM(pTm, &sTm);
16432 	}
16433 	/* Perform the requested operation */
16434 	switch(zFormat[0]){
16435 	case 'd':
16436 		/* Day of the month */
16437 		iVal = sTm.tm_mday;
16438 		break;
16439 	case 'h':
16440 		/*	Hour (12 hour format)*/
16441 		iVal = 1 + (sTm.tm_hour % 12);
16442 		break;
16443 	case 'H':
16444 		/* Hour (24 hour format)*/
16445 		iVal = sTm.tm_hour;
16446 		break;
16447 	case 'i':
16448 		/*Minutes*/
16449 		iVal = sTm.tm_min;
16450 		break;
16451 	case 'I':
16452 		/*	returns 1 if DST is activated, 0 otherwise */
16453 #ifdef __WINNT__
16454 #ifdef _MSC_VER
16455 #ifndef _WIN32_WCE
16456 			_get_daylight(&sTm.tm_isdst);
16457 #endif
16458 #endif
16459 #endif
16460 		iVal = sTm.tm_isdst;
16461 		break;
16462 	case 'L':
16463 		/* 	returns 1 for leap year, 0 otherwise */
16464 		iVal = IS_LEAP_YEAR(sTm.tm_year);
16465 		break;
16466 	case 'm':
16467 		/* Month number*/
16468 		iVal = sTm.tm_mon;
16469 		break;
16470 	case 's':
16471 		/*Seconds*/
16472 		iVal = sTm.tm_sec;
16473 		break;
16474 	case 't':{
16475 		/*Days in current month*/
16476 		static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
16477 		int nDays = aMonDays[sTm.tm_mon % 12 ];
16478 		if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
16479 			nDays = 28;
16480 		}
16481 		iVal = nDays;
16482 		break;
16483 			 }
16484 	case 'U':
16485 		/*Seconds since the Unix Epoch*/
16486 		iVal = (jx9_int64)time(0);
16487 		break;
16488 	case 'w':
16489 		/*	Day of the week (0 on Sunday) */
16490 		iVal = sTm.tm_wday;
16491 		break;
16492 	case 'W': {
16493 		/* ISO-8601 week number of year, weeks starting on Monday */
16494 		static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
16495 		iVal = aISO8601[sTm.tm_wday % 7 ];
16496 		break;
16497 			  }
16498 	case 'y':
16499 		/* Year (2 digits) */
16500 		iVal = sTm.tm_year % 100;
16501 		break;
16502 	case 'Y':
16503 		/* Year (4 digits) */
16504 		iVal = sTm.tm_year;
16505 		break;
16506 	case 'z':
16507 		/* Day of the year */
16508 		iVal = sTm.tm_yday;
16509 		break;
16510 	case 'Z':
16511 		/*Timezone offset in seconds*/
16512 		iVal = sTm.tm_gmtoff;
16513 		break;
16514 	default:
16515 		/* unknown format, throw a warning */
16516 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
16517 		break;
16518 	}
16519 	/* Return the time value */
16520 	jx9_result_int64(pCtx, iVal);
16521 	return JX9_OK;
16522 }
16523 /*
16524  * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s")
16525  *  [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
16526  *  Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer
16527  *  containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
16528  *  specified.
16529  *  Arguments may be left out in order from right to left; any arguments thus omitted will be set to
16530  *  the current value according to the local date and time.
16531  * Parameters
16532  * $hour
16533  *  The number of the hour relevant to the start of the day determined by month, day and year.
16534  *  Negative values reference the hour before midnight of the day in question. Values greater
16535  *  than 23 reference the appropriate hour in the following day(s).
16536  * $minute
16537  *  The number of the minute relevant to the start of the hour. Negative values reference
16538  *  the minute in the previous hour. Values greater than 59 reference the appropriate minute
16539  *  in the following hour(s).
16540  * $second
16541  *  The number of seconds relevant to the start of the minute. Negative values reference
16542  *  the second in the previous minute. Values greater than 59 reference the appropriate
16543  * second in the following minute(s).
16544  * $month
16545  *  The number of the month relevant to the end of the previous year. Values 1 to 12 reference
16546  *  the normal calendar months of the year in question. Values less than 1 (including negative values)
16547  *  reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
16548  * $day
16549  *  The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31
16550  *  (depending upon the month) reference the normal days in the relevant month. Values less than 1
16551  *  (including negative values) reference the days in the previous month, so 0 is the last day
16552  *  of the previous month, -1 is the day before that, etc. Values greater than the number of days
16553  *  in the relevant month reference the appropriate day in the following month(s).
16554  * $year
16555  *  The number of the year, may be a two or four digit value, with values between 0-69 mapping
16556  *  to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as
16557  *  most common today, the valid range for year is somewhere between 1901 and 2038.
16558  * $is_dst
16559  *  This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not,
16560  *  or -1 (the default) if it is unknown whether the time is within daylight savings time or not.
16561  * Return
16562  *   mktime() returns the Unix timestamp of the arguments given.
16563  *   If the arguments are invalid, the function returns FALSE
16564  */
jx9Builtin_mktime(jx9_context * pCtx,int nArg,jx9_value ** apArg)16565 static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
16566 {
16567 	const char *zFunction;
16568 	jx9_int64 iVal = 0;
16569 	struct tm *pTm;
16570 	time_t t;
16571 	/* Extract function name */
16572 	zFunction = jx9_function_name(pCtx);
16573 	/* Get the current time */
16574 	time(&t);
16575 	if( zFunction[0] == 'g' /* gmmktime */ ){
16576 		pTm = gmtime(&t);
16577 	}else{
16578 		/* localtime */
16579 		pTm = localtime(&t);
16580 	}
16581 	if( nArg > 0 ){
16582 		int iVal;
16583 		/* Hour */
16584 		iVal = jx9_value_to_int(apArg[0]);
16585 		pTm->tm_hour = iVal;
16586 		if( nArg > 1 ){
16587 			/* Minutes */
16588 			iVal = jx9_value_to_int(apArg[1]);
16589 			pTm->tm_min = iVal;
16590 			if( nArg > 2 ){
16591 				/* Seconds */
16592 				iVal = jx9_value_to_int(apArg[2]);
16593 				pTm->tm_sec = iVal;
16594 				if( nArg > 3 ){
16595 					/* Month */
16596 					iVal = jx9_value_to_int(apArg[3]);
16597 					pTm->tm_mon = iVal - 1;
16598 					if( nArg > 4 ){
16599 						/* mday */
16600 						iVal = jx9_value_to_int(apArg[4]);
16601 						pTm->tm_mday = iVal;
16602 						if( nArg > 5 ){
16603 							/* Year */
16604 							iVal = jx9_value_to_int(apArg[5]);
16605 							if( iVal > 1900 ){
16606 								iVal -= 1900;
16607 							}
16608 							pTm->tm_year = iVal;
16609 							if( nArg > 6 ){
16610 								/* is_dst */
16611 								iVal = jx9_value_to_bool(apArg[6]);
16612 								pTm->tm_isdst = iVal;
16613 							}
16614 						}
16615 					}
16616 				}
16617 			}
16618 		}
16619 	}
16620 	/* Make the time */
16621 	iVal = (jx9_int64)mktime(pTm);
16622 	/* Return the timesatmp as a 64bit integer */
16623 	jx9_result_int64(pCtx, iVal);
16624 	return JX9_OK;
16625 }
16626 /*
16627  * Section:
16628  *    URL handling Functions.
16629  * Authors:
16630  *    Symisc Systems, devel@symisc.net.
16631  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
16632  * Status:
16633  *    Stable.
16634  */
16635 /*
16636  * Output consumer callback for the standard Symisc routines.
16637  * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
16638  */
Consumer(const void * pData,unsigned int nLen,void * pUserData)16639 static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
16640 {
16641 	/* Store in the call context result buffer */
16642 	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
16643 	return SXRET_OK;
16644 }
16645 /*
16646  * string base64_encode(string $data)
16647  * string convert_uuencode(string $data)
16648  *  Encodes data with MIME base64
16649  * Parameter
16650  *  $data
16651  *    Data to encode
16652  * Return
16653  *  Encoded data or FALSE on failure.
16654  */
jx9Builtin_base64_encode(jx9_context * pCtx,int nArg,jx9_value ** apArg)16655 static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16656 {
16657 	const char *zIn;
16658 	int nLen;
16659 	if( nArg < 1 ){
16660 		/* Missing arguments, return FALSE */
16661 		jx9_result_bool(pCtx, 0);
16662 		return JX9_OK;
16663 	}
16664 	/* Extract the input string */
16665 	zIn = jx9_value_to_string(apArg[0], &nLen);
16666 	if( nLen < 1 ){
16667 		/* Nothing to process, return FALSE */
16668 		jx9_result_bool(pCtx, 0);
16669 		return JX9_OK;
16670 	}
16671 	/* Perform the BASE64 encoding */
16672 	SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
16673 	return JX9_OK;
16674 }
16675 /*
16676  * string base64_decode(string $data)
16677  * string convert_uudecode(string $data)
16678  *  Decodes data encoded with MIME base64
16679  * Parameter
16680  *  $data
16681  *    Encoded data.
16682  * Return
16683  *  Returns the original data or FALSE on failure.
16684  */
jx9Builtin_base64_decode(jx9_context * pCtx,int nArg,jx9_value ** apArg)16685 static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16686 {
16687 	const char *zIn;
16688 	int nLen;
16689 	if( nArg < 1 ){
16690 		/* Missing arguments, return FALSE */
16691 		jx9_result_bool(pCtx, 0);
16692 		return JX9_OK;
16693 	}
16694 	/* Extract the input string */
16695 	zIn = jx9_value_to_string(apArg[0], &nLen);
16696 	if( nLen < 1 ){
16697 		/* Nothing to process, return FALSE */
16698 		jx9_result_bool(pCtx, 0);
16699 		return JX9_OK;
16700 	}
16701 	/* Perform the BASE64 decoding */
16702 	SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
16703 	return JX9_OK;
16704 }
16705 /*
16706  * string urlencode(string $str)
16707  *  URL encoding
16708  * Parameter
16709  *  $data
16710  *   Input string.
16711  * Return
16712  *  Returns a string in which all non-alphanumeric characters except -_. have
16713  *  been replaced with a percent (%) sign followed by two hex digits and spaces
16714  *  encoded as plus (+) signs.
16715  */
jx9Builtin_urlencode(jx9_context * pCtx,int nArg,jx9_value ** apArg)16716 static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16717 {
16718 	const char *zIn;
16719 	int nLen;
16720 	if( nArg < 1 ){
16721 		/* Missing arguments, return FALSE */
16722 		jx9_result_bool(pCtx, 0);
16723 		return JX9_OK;
16724 	}
16725 	/* Extract the input string */
16726 	zIn = jx9_value_to_string(apArg[0], &nLen);
16727 	if( nLen < 1 ){
16728 		/* Nothing to process, return FALSE */
16729 		jx9_result_bool(pCtx, 0);
16730 		return JX9_OK;
16731 	}
16732 	/* Perform the URL encoding */
16733 	SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
16734 	return JX9_OK;
16735 }
16736 /*
16737  * string urldecode(string $str)
16738  *  Decodes any %## encoding in the given string.
16739  *  Plus symbols ('+') are decoded to a space character.
16740  * Parameter
16741  *  $data
16742  *    Input string.
16743  * Return
16744  *  Decoded URL or FALSE on failure.
16745  */
jx9Builtin_urldecode(jx9_context * pCtx,int nArg,jx9_value ** apArg)16746 static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
16747 {
16748 	const char *zIn;
16749 	int nLen;
16750 	if( nArg < 1 ){
16751 		/* Missing arguments, return FALSE */
16752 		jx9_result_bool(pCtx, 0);
16753 		return JX9_OK;
16754 	}
16755 	/* Extract the input string */
16756 	zIn = jx9_value_to_string(apArg[0], &nLen);
16757 	if( nLen < 1 ){
16758 		/* Nothing to process, return FALSE */
16759 		jx9_result_bool(pCtx, 0);
16760 		return JX9_OK;
16761 	}
16762 	/* Perform the URL decoding */
16763 	SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
16764 	return JX9_OK;
16765 }
16766 #endif /* JX9_DISABLE_BUILTIN_FUNC */
16767 /* Table of the built-in functions */
16768 static const jx9_builtin_func aBuiltInFunc[] = {
16769 	   /* Variable handling functions */
16770 	{ "is_bool"    , jx9Builtin_is_bool     },
16771 	{ "is_float"   , jx9Builtin_is_float    },
16772 	{ "is_real"    , jx9Builtin_is_float    },
16773 	{ "is_double"  , jx9Builtin_is_float    },
16774 	{ "is_int"     , jx9Builtin_is_int      },
16775 	{ "is_integer" , jx9Builtin_is_int      },
16776 	{ "is_long"    , jx9Builtin_is_int      },
16777 	{ "is_string"  , jx9Builtin_is_string   },
16778 	{ "is_null"    , jx9Builtin_is_null     },
16779 	{ "is_numeric" , jx9Builtin_is_numeric  },
16780 	{ "is_scalar"  , jx9Builtin_is_scalar   },
16781 	{ "is_array"   , jx9Builtin_is_array    },
16782 	{ "is_object"  , jx9Builtin_is_object   },
16783 	{ "is_resource", jx9Builtin_is_resource },
16784 	{ "douleval"   , jx9Builtin_floatval    },
16785 	{ "floatval"   , jx9Builtin_floatval    },
16786 	{ "intval"     , jx9Builtin_intval      },
16787 	{ "strval"     , jx9Builtin_strval      },
16788 	{ "empty"      , jx9Builtin_empty       },
16789 #ifndef JX9_DISABLE_BUILTIN_FUNC
16790 #ifdef JX9_ENABLE_MATH_FUNC
16791 	   /* Math functions */
16792 	{ "abs"  ,    jx9Builtin_abs          },
16793 	{ "sqrt" ,    jx9Builtin_sqrt         },
16794 	{ "exp"  ,    jx9Builtin_exp          },
16795 	{ "floor",    jx9Builtin_floor        },
16796 	{ "cos"  ,    jx9Builtin_cos          },
16797 	{ "sin"  ,    jx9Builtin_sin          },
16798 	{ "acos" ,    jx9Builtin_acos         },
16799 	{ "asin" ,    jx9Builtin_asin         },
16800 	{ "cosh" ,    jx9Builtin_cosh         },
16801 	{ "sinh" ,    jx9Builtin_sinh         },
16802 	{ "ceil" ,    jx9Builtin_ceil         },
16803 	{ "tan"  ,    jx9Builtin_tan          },
16804 	{ "tanh" ,    jx9Builtin_tanh         },
16805 	{ "atan" ,    jx9Builtin_atan         },
16806 	{ "atan2",    jx9Builtin_atan2        },
16807 	{ "log"  ,    jx9Builtin_log          },
16808 	{ "log10" ,   jx9Builtin_log10        },
16809 	{ "pow"  ,    jx9Builtin_pow          },
16810 	{ "pi",       jx9Builtin_pi           },
16811 	{ "fmod",     jx9Builtin_fmod         },
16812 	{ "hypot",    jx9Builtin_hypot        },
16813 #endif /* JX9_ENABLE_MATH_FUNC */
16814 	{ "round",    jx9Builtin_round        },
16815 	{ "dechex", jx9Builtin_dechex         },
16816 	{ "decoct", jx9Builtin_decoct         },
16817 	{ "decbin", jx9Builtin_decbin         },
16818 	{ "hexdec", jx9Builtin_hexdec         },
16819 	{ "bindec", jx9Builtin_bindec         },
16820 	{ "octdec", jx9Builtin_octdec         },
16821 	{ "base_convert", jx9Builtin_base_convert },
16822 	   /* String handling functions */
16823 	{ "substr",          jx9Builtin_substr     },
16824 	{ "substr_compare",  jx9Builtin_substr_compare },
16825 	{ "substr_count",    jx9Builtin_substr_count },
16826 	{ "chunk_split",     jx9Builtin_chunk_split},
16827 	{ "htmlspecialchars", jx9Builtin_htmlspecialchars },
16828 	{ "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode },
16829 	{ "get_html_translation_table", jx9Builtin_get_html_translation_table },
16830 	{ "htmlentities", jx9Builtin_htmlentities},
16831 	{ "html_entity_decode", jx9Builtin_html_entity_decode},
16832 	{ "strlen"     , jx9Builtin_strlen     },
16833 	{ "strcmp"     , jx9Builtin_strcmp     },
16834 	{ "strcoll"    , jx9Builtin_strcmp     },
16835 	{ "strncmp"    , jx9Builtin_strncmp    },
16836 	{ "strcasecmp" , jx9Builtin_strcasecmp },
16837 	{ "strncasecmp", jx9Builtin_strncasecmp},
16838 	{ "implode"    , jx9Builtin_implode    },
16839 	{ "join"       , jx9Builtin_implode    },
16840 	{ "implode_recursive" , jx9Builtin_implode_recursive },
16841 	{ "join_recursive"    , jx9Builtin_implode_recursive },
16842 	{ "explode"     , jx9Builtin_explode    },
16843 	{ "trim"        , jx9Builtin_trim       },
16844 	{ "rtrim"       , jx9Builtin_rtrim      },
16845 	{ "chop"        , jx9Builtin_rtrim      },
16846 	{ "ltrim"       , jx9Builtin_ltrim      },
16847 	{ "strtolower",   jx9Builtin_strtolower },
16848 	{ "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
16849 	{ "strtoupper",   jx9Builtin_strtoupper },
16850 	{ "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
16851 	{ "ord",          jx9Builtin_ord        },
16852 	{ "chr",          jx9Builtin_chr        },
16853 	{ "bin2hex",      jx9Builtin_bin2hex    },
16854 	{ "strstr",       jx9Builtin_strstr     },
16855 	{ "stristr",      jx9Builtin_stristr    },
16856 	{ "strchr",       jx9Builtin_strstr     },
16857 	{ "strpos",       jx9Builtin_strpos     },
16858 	{ "stripos",      jx9Builtin_stripos    },
16859 	{ "strrpos",      jx9Builtin_strrpos    },
16860 	{ "strripos",     jx9Builtin_strripos   },
16861 	{ "strrchr",      jx9Builtin_strrchr    },
16862 	{ "strrev",       jx9Builtin_strrev     },
16863 	{ "str_repeat",   jx9Builtin_str_repeat },
16864 	{ "nl2br",        jx9Builtin_nl2br      },
16865 	{ "sprintf",      jx9Builtin_sprintf    },
16866 	{ "printf",       jx9Builtin_printf     },
16867 	{ "vprintf",      jx9Builtin_vprintf    },
16868 	{ "vsprintf",     jx9Builtin_vsprintf   },
16869 	{ "size_format",  jx9Builtin_size_format},
16870 #if !defined(JX9_DISABLE_HASH_FUNC)
16871 	{ "md5",          jx9Builtin_md5       },
16872 	{ "sha1",         jx9Builtin_sha1      },
16873 	{ "crc32",        jx9Builtin_crc32     },
16874 #endif /* JX9_DISABLE_HASH_FUNC */
16875 	{ "str_getcsv",   jx9Builtin_str_getcsv },
16876 	{ "strip_tags",   jx9Builtin_strip_tags },
16877 	{ "str_split",    jx9Builtin_str_split  },
16878 	{ "strspn",       jx9Builtin_strspn     },
16879 	{ "strcspn",      jx9Builtin_strcspn    },
16880 	{ "strpbrk",      jx9Builtin_strpbrk    },
16881 	{ "soundex",      jx9Builtin_soundex    },
16882 	{ "wordwrap",     jx9Builtin_wordwrap   },
16883 	{ "strtok",       jx9Builtin_strtok     },
16884 	{ "str_pad",      jx9Builtin_str_pad    },
16885 	{ "str_replace",  jx9Builtin_str_replace},
16886 	{ "str_ireplace", jx9Builtin_str_replace},
16887 	{ "strtr",        jx9Builtin_strtr      },
16888 	{ "parse_ini_string", jx9Builtin_parse_ini_string},
16889 	         /* Ctype functions */
16890 	{ "ctype_alnum", jx9Builtin_ctype_alnum },
16891 	{ "ctype_alpha", jx9Builtin_ctype_alpha },
16892 	{ "ctype_cntrl", jx9Builtin_ctype_cntrl },
16893 	{ "ctype_digit", jx9Builtin_ctype_digit },
16894 	{ "ctype_xdigit", jx9Builtin_ctype_xdigit},
16895 	{ "ctype_graph", jx9Builtin_ctype_graph },
16896 	{ "ctype_print", jx9Builtin_ctype_print },
16897 	{ "ctype_punct", jx9Builtin_ctype_punct },
16898 	{ "ctype_space", jx9Builtin_ctype_space },
16899 	{ "ctype_lower", jx9Builtin_ctype_lower },
16900 	{ "ctype_upper", jx9Builtin_ctype_upper },
16901 	         /* Time functions */
16902 	{ "time"    ,    jx9Builtin_time         },
16903 	{ "microtime",   jx9Builtin_microtime    },
16904 	{ "getdate" ,    jx9Builtin_getdate      },
16905 	{ "gettimeofday", jx9Builtin_gettimeofday },
16906 	{ "date",        jx9Builtin_date         },
16907 	{ "strftime",    jx9Builtin_strftime     },
16908 	{ "idate",       jx9Builtin_idate        },
16909 	{ "gmdate",      jx9Builtin_gmdate       },
16910 	{ "localtime",   jx9Builtin_localtime    },
16911 	{ "mktime",      jx9Builtin_mktime       },
16912 	{ "gmmktime",    jx9Builtin_mktime       },
16913 	        /* URL functions */
16914 	{ "base64_encode", jx9Builtin_base64_encode },
16915 	{ "base64_decode", jx9Builtin_base64_decode },
16916 	{ "convert_uuencode", jx9Builtin_base64_encode },
16917 	{ "convert_uudecode", jx9Builtin_base64_decode },
16918 	{ "urlencode",    jx9Builtin_urlencode },
16919 	{ "urldecode",    jx9Builtin_urldecode },
16920 	{ "rawurlencode", jx9Builtin_urlencode },
16921 	{ "rawurldecode", jx9Builtin_urldecode },
16922 #endif /* JX9_DISABLE_BUILTIN_FUNC */
16923 };
16924 /*
16925  * Register the built-in functions defined above, the array functions
16926  * defined in hashmap.c and the IO functions defined in vfs.c.
16927  */
jx9RegisterBuiltInFunction(jx9_vm * pVm)16928 JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
16929 {
16930 	sxu32 n;
16931 	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
16932 		jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
16933 	}
16934 	/* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
16935 	jx9RegisterHashmapFunctions(&(*pVm));
16936 	/* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
16937 	jx9RegisterIORoutine(&(*pVm));
16938 }
16939 
16940 /* jx9_compile.c */
16941 /*
16942  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
16943  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
16944  * Version 1.7.2
16945  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
16946  * please contact Symisc Systems via:
16947  *       legal@symisc.net
16948  *       licensing@symisc.net
16949  *       contact@symisc.net
16950  * or visit:
16951  *      http://jx9.symisc.net/
16952  */
16953  /* $SymiscID: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
16954 #ifndef JX9_AMALGAMATION
16955 #include "jx9Int.h"
16956 #endif
16957 /*
16958  * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
16959  * That is, routines defined in this file takes a stream of tokens and output
16960  * JX9 bytecode instructions.
16961  */
16962 /* Forward declaration */
16963 typedef struct LangConstruct LangConstruct;
16964 typedef struct JumpFixup     JumpFixup;
16965 /* Block [i.e: set of statements] control flags */
16966 #define GEN_BLOCK_LOOP        0x001    /* Loop block [i.e: for, while, ...] */
16967 #define GEN_BLOCK_PROTECTED   0x002    /* Protected block */
16968 #define GEN_BLOCK_COND        0x004    /* Conditional block [i.e: if(condition){} ]*/
16969 #define GEN_BLOCK_FUNC        0x008    /* Function body */
16970 #define GEN_BLOCK_GLOBAL      0x010    /* Global block (always set)*/
16971 #define GEN_BLOC_NESTED_FUNC  0x020    /* Nested function body */
16972 #define GEN_BLOCK_EXPR        0x040    /* Expression */
16973 #define GEN_BLOCK_STD         0x080    /* Standard block */
16974 #define GEN_BLOCK_SWITCH      0x100    /* Switch statement */
16975 /*
16976  * Compilation of some JX9 constructs such as if, for, while, the logical or
16977  * (||) and logical and (&&) operators in expressions requires the
16978  * generation of forward jumps.
16979  * Since the destination PC target of these jumps isn't known when the jumps
16980  * are emitted, we record each forward jump in an instance of the following
16981  * structure. Those jumps are fixed later when the jump destination is resolved.
16982  */
16983 struct JumpFixup
16984 {
16985 	sxi32 nJumpType;     /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
16986 	sxu32 nInstrIdx;     /* Instruction index to fix later when the jump destination is resolved. */
16987 };
16988 /*
16989  * Each language construct is represented by an instance
16990  * of the following structure.
16991  */
16992 struct LangConstruct
16993 {
16994 	sxu32 nID;                     /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
16995 	ProcLangConstruct xConstruct;  /* C function implementing the language construct */
16996 };
16997 /* Compilation flags */
16998 #define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
16999 /* Token stream synchronization macros */
17000 #define SWAP_TOKEN_STREAM(GEN, START, END)\
17001 	pTmp  = GEN->pEnd;\
17002 	pGen->pIn  = START;\
17003 	pGen->pEnd = END
17004 #define UPDATE_TOKEN_STREAM(GEN)\
17005 	if( GEN->pIn < pTmp ){\
17006 	    GEN->pIn++;\
17007 	}\
17008 	GEN->pEnd = pTmp
17009 #define SWAP_DELIMITER(GEN, START, END)\
17010 	pTmpIn  = GEN->pIn;\
17011 	pTmpEnd = GEN->pEnd;\
17012 	GEN->pIn = START;\
17013 	GEN->pEnd = END
17014 #define RE_SWAP_DELIMITER(GEN)\
17015 	GEN->pIn  = pTmpIn;\
17016 	GEN->pEnd = pTmpEnd
17017 /* Flags related to expression compilation */
17018 #define EXPR_FLAG_LOAD_IDX_STORE    0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
17019 #define EXPR_FLAG_RDONLY_LOAD       0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
17020 #define EXPR_FLAG_COMMA_STATEMENT   0x004 /* Treat comma expression as a single statement (used by object attributes) */
17021 /* Forward declaration */
17022 static sxi32 jx9CompileExpr(
17023 	jx9_gen_state *pGen, /* Code generator state */
17024 	sxi32 iFlags,        /* Control flags */
17025 	sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
17026 	);
17027 
17028 /*
17029  * Recover from a compile-time error. In other words synchronize
17030  * the token stream cursor with the first semi-colon seen.
17031  */
jx9ErrorRecover(jx9_gen_state * pGen)17032 static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
17033 {
17034 	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
17035 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
17036 		pGen->pIn++;
17037 	}
17038 	return SXRET_OK;
17039 }
17040 /*
17041  * Check if the given identifier name is reserved or not.
17042  * Return TRUE if reserved.FALSE otherwise.
17043  */
GenStateIsReservedID(SyString * pName)17044 static int GenStateIsReservedID(SyString *pName)
17045 {
17046 	if( pName->nByte == sizeof("null") - 1 ){
17047 		if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
17048 			return TRUE;
17049 		}else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
17050 			return TRUE;
17051 		}
17052 	}else if( pName->nByte == sizeof("false") - 1 ){
17053 		if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
17054 			return TRUE;
17055 		}
17056 	}
17057 	/* Not a reserved constant */
17058 	return FALSE;
17059 }
17060 /*
17061  * Check if a given token value is installed in the literal table.
17062  */
GenStateFindLiteral(jx9_gen_state * pGen,const SyString * pValue,sxu32 * pIdx)17063 static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
17064 {
17065 	SyHashEntry *pEntry;
17066 	pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
17067 	if( pEntry == 0 ){
17068 		return SXERR_NOTFOUND;
17069 	}
17070 	*pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
17071 	return SXRET_OK;
17072 }
17073 /*
17074  * Install a given constant index in the literal table.
17075  * In order to be installed, the jx9_value must be of type string.
17076  */
GenStateInstallLiteral(jx9_gen_state * pGen,jx9_value * pObj,sxu32 nIdx)17077 static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
17078 {
17079 	if( SyBlobLength(&pObj->sBlob) > 0 ){
17080 		SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
17081 	}
17082 	return SXRET_OK;
17083 }
17084 /*
17085  * Generate a fatal error.
17086  */
GenStateOutOfMem(jx9_gen_state * pGen)17087 static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
17088 {
17089 	jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
17090 	/* Abort compilation immediately */
17091 	return SXERR_ABORT;
17092 }
17093 /*
17094  * Fetch a block that correspond to the given criteria from the stack of
17095  * compiled blocks.
17096  * Return a pointer to that block on success. NULL otherwise.
17097  */
GenStateFetchBlock(GenBlock * pCurrent,sxi32 iBlockType,sxi32 iCount)17098 static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
17099 {
17100 	GenBlock *pBlock = pCurrent;
17101 	for(;;){
17102 		if( pBlock->iFlags & iBlockType ){
17103 			iCount--; /* Decrement nesting level */
17104 			if( iCount < 1 ){
17105 				/* Block meet with the desired criteria */
17106 				return pBlock;
17107 			}
17108 		}
17109 		/* Point to the upper block */
17110 		pBlock = pBlock->pParent;
17111 		if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
17112 			/* Forbidden */
17113 			break;
17114 		}
17115 	}
17116 	/* No such block */
17117 	return 0;
17118 }
17119 /*
17120  * Initialize a freshly allocated block instance.
17121  */
GenStateInitBlock(jx9_gen_state * pGen,GenBlock * pBlock,sxi32 iType,sxu32 nFirstInstr,void * pUserData)17122 static void GenStateInitBlock(
17123 	jx9_gen_state *pGen, /* Code generator state */
17124 	GenBlock *pBlock,    /* Target block */
17125 	sxi32 iType,         /* Block type [i.e: loop, conditional, function body, etc.]*/
17126 	sxu32 nFirstInstr,   /* First instruction to compile */
17127 	void *pUserData      /* Upper layer private data */
17128 	)
17129 {
17130 	/* Initialize block fields */
17131 	pBlock->nFirstInstr = nFirstInstr;
17132 	pBlock->pUserData   = pUserData;
17133 	pBlock->pGen        = pGen;
17134 	pBlock->iFlags      = iType;
17135 	pBlock->pParent     = 0;
17136 	pBlock->bPostContinue = 0;
17137 	SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
17138 	SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
17139 }
17140 /*
17141  * Allocate a new block instance.
17142  * Return SXRET_OK and write a pointer to the new instantiated block
17143  * on success.Otherwise generate a compile-time error and abort
17144  * processing on failure.
17145  */
GenStateEnterBlock(jx9_gen_state * pGen,sxi32 iType,sxu32 nFirstInstr,void * pUserData,GenBlock ** ppBlock)17146 static sxi32 GenStateEnterBlock(
17147 	jx9_gen_state *pGen,  /* Code generator state */
17148 	sxi32 iType,          /* Block type [i.e: loop, conditional, function body, etc.]*/
17149 	sxu32 nFirstInstr,    /* First instruction to compile */
17150 	void *pUserData,      /* Upper layer private data */
17151 	GenBlock **ppBlock    /* OUT: instantiated block */
17152 	)
17153 {
17154 	GenBlock *pBlock;
17155 	/* Allocate a new block instance */
17156 	pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
17157 	if( pBlock == 0 ){
17158 		/* If the supplied memory subsystem is so sick that we are unable to allocate
17159 		 * a tiny chunk of memory, there is no much we can do here.
17160 		 */
17161 		return GenStateOutOfMem(pGen);
17162 	}
17163 	/* Zero the structure */
17164 	SyZero(pBlock, sizeof(GenBlock));
17165 	GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
17166 	/* Link to the parent block */
17167 	pBlock->pParent = pGen->pCurrent;
17168 	/* Mark as the current block */
17169 	pGen->pCurrent = pBlock;
17170 	if( ppBlock ){
17171 		/* Write a pointer to the new instance */
17172 		*ppBlock = pBlock;
17173 	}
17174 	return SXRET_OK;
17175 }
17176 /*
17177  * Release block fields without freeing the whole instance.
17178  */
GenStateReleaseBlock(GenBlock * pBlock)17179 static void GenStateReleaseBlock(GenBlock *pBlock)
17180 {
17181 	SySetRelease(&pBlock->aPostContFix);
17182 	SySetRelease(&pBlock->aJumpFix);
17183 }
17184 /*
17185  * Release a block.
17186  */
GenStateFreeBlock(GenBlock * pBlock)17187 static void GenStateFreeBlock(GenBlock *pBlock)
17188 {
17189 	jx9_gen_state *pGen = pBlock->pGen;
17190 	GenStateReleaseBlock(&(*pBlock));
17191 	/* Free the instance */
17192 	SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
17193 }
17194 /*
17195  * POP and release a block from the stack of compiled blocks.
17196  */
GenStateLeaveBlock(jx9_gen_state * pGen,GenBlock ** ppBlock)17197 static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
17198 {
17199 	GenBlock *pBlock = pGen->pCurrent;
17200 	if( pBlock == 0 ){
17201 		/* No more block to pop */
17202 		return SXERR_EMPTY;
17203 	}
17204 	/* Point to the upper block */
17205 	pGen->pCurrent = pBlock->pParent;
17206 	if( ppBlock ){
17207 		/* Write a pointer to the popped block */
17208 		*ppBlock = pBlock;
17209 	}else{
17210 		/* Safely release the block */
17211 		GenStateFreeBlock(&(*pBlock));
17212 	}
17213 	return SXRET_OK;
17214 }
17215 /*
17216  * Emit a forward jump.
17217  * Notes on forward jumps
17218  *  Compilation of some JX9 constructs such as if, for, while and the logical or
17219  *  (||) and logical and (&&) operators in expressions requires the
17220  *  generation of forward jumps.
17221  *  Since the destination PC target of these jumps isn't known when the jumps
17222  *  are emitted, we record each forward jump in an instance of the following
17223  *  structure. Those jumps are fixed later when the jump destination is resolved.
17224  */
GenStateNewJumpFixup(GenBlock * pBlock,sxi32 nJumpType,sxu32 nInstrIdx)17225 static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
17226 {
17227 	JumpFixup sJumpFix;
17228 	sxi32 rc;
17229 	/* Init the JumpFixup structure */
17230 	sJumpFix.nJumpType = nJumpType;
17231 	sJumpFix.nInstrIdx = nInstrIdx;
17232 	/* Insert in the jump fixup table */
17233 	rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
17234 	return rc;
17235 }
17236 /*
17237  * Fix a forward jump now the jump destination is resolved.
17238  * Return the total number of fixed jumps.
17239  * Notes on forward jumps:
17240  *  Compilation of some JX9 constructs such as if, for, while and the logical or
17241  *  (||) and logical and (&&) operators in expressions requires the
17242  *  generation of forward jumps.
17243  *  Since the destination PC target of these jumps isn't known when the jumps
17244  *  are emitted, we record each forward jump in an instance of the following
17245  *  structure.Those jumps are fixed later when the jump destination is resolved.
17246  */
GenStateFixJumps(GenBlock * pBlock,sxi32 nJumpType,sxu32 nJumpDest)17247 static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
17248 {
17249 	JumpFixup *aFix;
17250 	VmInstr *pInstr;
17251 	sxu32 nFixed;
17252 	sxu32 n;
17253 	/* Point to the jump fixup table */
17254 	aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
17255 	/* Fix the desired jumps */
17256 	for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
17257 		if( aFix[n].nJumpType < 0 ){
17258 			/* Already fixed */
17259 			continue;
17260 		}
17261 		if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
17262 			/* Not of our interest */
17263 			continue;
17264 		}
17265 		/* Point to the instruction to fix */
17266 		pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
17267 		if( pInstr ){
17268 			pInstr->iP2 = nJumpDest;
17269 			nFixed++;
17270 			/* Mark as fixed */
17271 			aFix[n].nJumpType = -1;
17272 		}
17273 	}
17274 	/* Total number of fixed jumps */
17275 	return nFixed;
17276 }
17277 /*
17278  * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
17279  * in the constant table.
17280  */
GenStateInstallNumLiteral(jx9_gen_state * pGen,sxu32 * pIdx)17281 static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
17282 {
17283 	jx9_value *pObj;
17284 	sxu32 nIdx = 0; /* cc warning */
17285 	/* Reserve a new constant */
17286 	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17287 	if( pObj == 0 ){
17288 		GenStateOutOfMem(pGen);
17289 		return 0;
17290 	}
17291 	*pIdx = nIdx;
17292 	/* TODO(chems): Create a numeric table (64bit int keys) same as
17293 	 * the constant string iterals table [optimization purposes].
17294 	 */
17295 	return pObj;
17296 }
17297 /*
17298  * Compile a numeric [i.e: integer or real] literal.
17299  * Notes on the integer type.
17300  *  According to the JX9 language reference manual
17301  *  Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
17302  *  or binary (base 2) notation, optionally preceded by a sign (- or +).
17303  *  To use octal notation, precede the number with a 0 (zero). To use hexadecimal
17304  *  notation precede the number with 0x. To use binary notation precede the number with 0b.
17305  */
jx9CompileNumLiteral(jx9_gen_state * pGen,sxi32 iCompileFlag)17306 static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
17307 {
17308 	SyToken *pToken = pGen->pIn; /* Raw token */
17309 	sxu32 nIdx = 0;
17310 	if( pToken->nType & JX9_TK_INTEGER ){
17311 		jx9_value *pObj;
17312 		sxi64 iValue;
17313 		iValue = jx9TokenValueToInt64(&pToken->sData);
17314 		pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
17315 		if( pObj == 0 ){
17316 			SXUNUSED(iCompileFlag); /* cc warning */
17317 			return SXERR_ABORT;
17318 		}
17319 		jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
17320 	}else{
17321 		/* Real number */
17322 		jx9_value *pObj;
17323 		/* Reserve a new constant */
17324 		pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17325 		if( pObj == 0 ){
17326 			return GenStateOutOfMem(pGen);
17327 		}
17328 		jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
17329 		jx9MemObjToReal(pObj);
17330 	}
17331 	/* Emit the load constant instruction */
17332 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17333 	/* Node successfully compiled */
17334 	return SXRET_OK;
17335 }
17336 /*
17337  * Compile a nowdoc string.
17338  * According to the JX9 language reference manual:
17339  *
17340  *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
17341  *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
17342  *  The construct is ideal for embedding JX9 code or other large blocks of text without the
17343  *  need for escaping. It shares some features in common with the SGML <![CDATA[ ]]>
17344  *  construct, in that it declares a block of text which is not for parsing.
17345  *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
17346  *  which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc
17347  *  identifiers also apply to nowdoc identifiers, especially those regarding the appearance
17348  *  of the closing identifier.
17349  */
jx9CompileNowdoc(jx9_gen_state * pGen,sxi32 iCompileFlag)17350 static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
17351 {
17352 	SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
17353 	jx9_value *pObj;
17354 	sxu32 nIdx;
17355 	nIdx = 0; /* Prevent compiler warning */
17356 	if( pStr->nByte <= 0 ){
17357 		/* Empty string, load NULL */
17358 		jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
17359 		return SXRET_OK;
17360 	}
17361 	/* Reserve a new constant */
17362 	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17363 	if( pObj == 0 ){
17364 		jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
17365 		SXUNUSED(iCompileFlag); /* cc warning */
17366 		return SXERR_ABORT;
17367 	}
17368 	/* No processing is done here, simply a memcpy() operation */
17369 	jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
17370 	/* Emit the load constant instruction */
17371 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17372 	/* Node successfully compiled */
17373 	return SXRET_OK;
17374 }
17375 /*
17376  * Compile a single quoted string.
17377  * According to the JX9 language reference manual:
17378  *
17379  *   The simplest way to specify a string is to enclose it in single quotes (the character ' ).
17380  *   To specify a literal single quote, escape it with a backslash (\). To specify a literal
17381  *   backslash, double it (\\). All other instances of backslash will be treated as a literal
17382  *   backslash: this means that the other escape sequences you might be used to, such as \r
17383  *   or \n, will be output literally as specified rather than having any special meaning.
17384  *
17385  */
jx9CompileSimpleString(jx9_gen_state * pGen,sxi32 iCompileFlag)17386 JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
17387 {
17388 	SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
17389 	const char *zIn, *zCur, *zEnd;
17390 	jx9_value *pObj;
17391 	sxu32 nIdx;
17392 	nIdx = 0; /* Prevent compiler warning */
17393 	/* Delimit the string */
17394 	zIn  = pStr->zString;
17395 	zEnd = &zIn[pStr->nByte];
17396 	if( zIn > zEnd ){
17397 		/* Empty string, load NULL */
17398 		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17399 		return SXRET_OK;
17400 	}
17401 	if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
17402 		/* Already processed, emit the load constant instruction
17403 		 * and return.
17404 		 */
17405 		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17406 		return SXRET_OK;
17407 	}
17408 	/* Reserve a new constant */
17409 	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17410 	if( pObj == 0 ){
17411 		jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
17412 		SXUNUSED(iCompileFlag); /* cc warning */
17413 		return SXERR_ABORT;
17414 	}
17415 	jx9MemObjInitFromString(pGen->pVm, pObj, 0);
17416 	/* Compile the node */
17417 	for(;;){
17418 		if( zIn >= zEnd ){
17419 			/* End of input */
17420 			break;
17421 		}
17422 		zCur = zIn;
17423 		while( zIn < zEnd && zIn[0] != '\\' ){
17424 			zIn++;
17425 		}
17426 		if( zIn > zCur ){
17427 			/* Append raw contents*/
17428 			jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
17429 		}
17430         else
17431         {
17432             jx9MemObjStringAppend(pObj, "", 0);
17433         }
17434 		zIn++;
17435 		if( zIn < zEnd ){
17436 			if( zIn[0] == '\\' ){
17437 				/* A literal backslash */
17438 				jx9MemObjStringAppend(pObj, "\\", sizeof(char));
17439 			}else if( zIn[0] == '\'' ){
17440 				/* A single quote */
17441 				jx9MemObjStringAppend(pObj, "'", sizeof(char));
17442 			}else{
17443 				/* verbatim copy */
17444 				zIn--;
17445 				jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
17446 				zIn++;
17447 			}
17448 		}
17449 		/* Advance the stream cursor */
17450 		zIn++;
17451 	}
17452 	/* Emit the load constant instruction */
17453 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17454 	if( pStr->nByte < 1024 ){
17455 		/* Install in the literal table */
17456 		GenStateInstallLiteral(pGen, pObj, nIdx);
17457 	}
17458 	/* Node successfully compiled */
17459 	return SXRET_OK;
17460 }
17461 /*
17462  * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
17463  * According to the JX9 language reference manual
17464  *   When a string is specified in double quotes or with heredoc, variables are parsed within it.
17465  *  There are two types of syntax: a simple one and a complex one. The simple syntax is the most
17466  *  common and convenient. It provides a way to embed a variable, an array value, or an object
17467  *  property in a string with a minimum of effort.
17468  *  Simple syntax
17469  *   If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
17470  *   to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
17471  *   the end of the name.
17472  *   Similarly, an array index or an object property can be parsed. With array indices, the closing
17473  *   square bracket (]) marks the end of the index. The same rules apply to object properties
17474  *   as to simple variables.
17475  *  Complex (curly) syntax
17476  *   This isn't called complex because the syntax is complex, but because it allows for the use
17477  *   of complex expressions.
17478  *   Any scalar variable, array element or object property with a string representation can be
17479  *   included via this syntax. Simply write the expression the same way as it would appear outside
17480  *   the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
17481  *   be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
17482  */
GenStateProcessStringExpression(jx9_gen_state * pGen,const char * zIn,const char * zEnd)17483 static sxi32 GenStateProcessStringExpression(
17484 	jx9_gen_state *pGen, /* Code generator state */
17485 	const char *zIn,     /* Raw expression */
17486 	const char *zEnd     /* End of the expression */
17487 	)
17488 {
17489 	SyToken *pTmpIn, *pTmpEnd;
17490 	SySet sToken;
17491 	sxi32 rc;
17492 	/* Initialize the token set */
17493 	SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
17494 	/* Preallocate some slots */
17495 	SySetAlloc(&sToken, 0x08);
17496 	/* Tokenize the text */
17497 	jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
17498 	/* Swap delimiter */
17499 	pTmpIn  = pGen->pIn;
17500 	pTmpEnd = pGen->pEnd;
17501 	pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
17502 	pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
17503 	/* Compile the expression */
17504 	rc = jx9CompileExpr(&(*pGen), 0, 0);
17505 	/* Restore token stream */
17506 	pGen->pIn  = pTmpIn;
17507 	pGen->pEnd = pTmpEnd;
17508 	/* Release the token set */
17509 	SySetRelease(&sToken);
17510 	/* Compilation result */
17511 	return rc;
17512 }
17513 /*
17514  * Reserve a new constant for a double quoted/heredoc string.
17515  */
GenStateNewStrObj(jx9_gen_state * pGen,sxi32 * pCount)17516 static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
17517 {
17518 	jx9_value *pConstObj;
17519 	sxu32 nIdx = 0;
17520 	/* Reserve a new constant */
17521 	pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17522 	if( pConstObj == 0 ){
17523 		GenStateOutOfMem(&(*pGen));
17524 		return 0;
17525 	}
17526 	(*pCount)++;
17527 	jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
17528 	/* Emit the load constant instruction */
17529 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17530 	return pConstObj;
17531 }
17532 /*
17533  * Compile a double quoted/heredoc string.
17534  * According to the JX9 language reference manual
17535  * Heredoc
17536  *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
17537  *  is provided, then a newline. The string itself follows, and then the same identifier again
17538  *  to close the quotation.
17539  *  The closing identifier must begin in the first column of the line. Also, the identifier must
17540  *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric
17541  *  characters and underscores, and must start with a non-digit character or underscore.
17542  *  Warning
17543  *  It is very important to note that the line with the closing identifier must contain
17544  *  no other characters, except possibly a semicolon (;). That means especially that the identifier
17545  *  may not be indented, and there may not be any spaces or tabs before or after the semicolon.
17546  *  It's also important to realize that the first character before the closing identifier must
17547  *  be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
17548  *  The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
17549  *  If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
17550  *  identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
17551  *  the end of the current file, a parse error will result at the last line.
17552  *  Heredocs can not be used for initializing object properties.
17553  * Double quoted
17554  *  If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
17555  *  Escaped characters Sequence 	Meaning
17556  *  \n linefeed (LF or 0x0A (10) in ASCII)
17557  *  \r carriage return (CR or 0x0D (13) in ASCII)
17558  *  \t horizontal tab (HT or 0x09 (9) in ASCII)
17559  *  \v vertical tab (VT or 0x0B (11) in ASCII)
17560  *  \f form feed (FF or 0x0C (12) in ASCII)
17561  *  \\ backslash
17562  *  \$ dollar sign
17563  *  \" double-quote
17564  *  \[0-7]{1, 3} 	the sequence of characters matching the regular expression is a character in octal notation
17565  *  \x[0-9A-Fa-f]{1, 2} 	the sequence of characters matching the regular expression is a character in hexadecimal notation
17566  * As in single quoted strings, escaping any other character will result in the backslash being printed too.
17567  * The most important feature of double-quoted strings is the fact that variable names will be expanded.
17568  * See string parsing for details.
17569  */
GenStateCompileString(jx9_gen_state * pGen)17570 static sxi32 GenStateCompileString(jx9_gen_state *pGen)
17571 {
17572 	SyString *pStr = &pGen->pIn->sData; /* Raw token value */
17573 	const char *zIn, *zCur, *zEnd;
17574 	jx9_value *pObj = 0;
17575 	sxi32 iCons;
17576 	sxi32 rc;
17577 	/* Delimit the string */
17578 	zIn  = pStr->zString;
17579 	zEnd = &zIn[pStr->nByte];
17580 	if( zIn > zEnd ){
17581 		/* Empty string, load NULL */
17582 		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17583 		return SXRET_OK;
17584 	}
17585 	zCur = 0;
17586 	/* Compile the node */
17587 	iCons = 0;
17588 	for(;;){
17589 		zCur = zIn;
17590 		while( zIn < zEnd && zIn[0] != '\\'  ){
17591 			if(zIn[0] == '$' && &zIn[1] < zEnd &&
17592 				(((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
17593 					break;
17594 			}
17595 			zIn++;
17596 		}
17597 		if( zIn > zCur ){
17598 			if( pObj == 0 ){
17599 				pObj = GenStateNewStrObj(&(*pGen), &iCons);
17600 				if( pObj == 0 ){
17601 					return SXERR_ABORT;
17602 				}
17603 			}
17604 			jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
17605 		}
17606         else
17607         {
17608             if( pObj == 0 ){
17609                 pObj = GenStateNewStrObj(&(*pGen), &iCons);
17610                 if( pObj == 0 ){
17611                     return SXERR_ABORT;
17612                 }
17613             }
17614             jx9MemObjStringAppend(pObj, "", 0);
17615         }
17616 		if( zIn >= zEnd ){
17617 			break;
17618 		}
17619 		if( zIn[0] == '\\' ){
17620 			const char *zPtr = 0;
17621 			sxu32 n;
17622 			zIn++;
17623 			if( zIn >= zEnd ){
17624 				break;
17625 			}
17626 			if( pObj == 0 ){
17627 				pObj = GenStateNewStrObj(&(*pGen), &iCons);
17628 				if( pObj == 0 ){
17629 					return SXERR_ABORT;
17630 				}
17631 			}
17632 			n = sizeof(char); /* size of conversion */
17633 			switch( zIn[0] ){
17634 			case '$':
17635 				/* Dollar sign */
17636 				jx9MemObjStringAppend(pObj, "$", sizeof(char));
17637 				break;
17638 			case '\\':
17639 				/* A literal backslash */
17640 				jx9MemObjStringAppend(pObj, "\\", sizeof(char));
17641 				break;
17642 			case 'a':
17643 				/* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
17644 				jx9MemObjStringAppend(pObj, "\a", sizeof(char));
17645 				break;
17646 			case 'b':
17647 				/* Backspace (BS)[ctrl+h] ASCII code 8 */
17648 				jx9MemObjStringAppend(pObj, "\b", sizeof(char));
17649 				break;
17650 			case 'f':
17651 				/* Form-feed (FF)[ctrl+l] ASCII code 12 */
17652 				jx9MemObjStringAppend(pObj, "\f", sizeof(char));
17653 				break;
17654 			case 'n':
17655 				/* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
17656 				jx9MemObjStringAppend(pObj, "\n", sizeof(char));
17657 				break;
17658 			case 'r':
17659 				/* Carriage return (CR)[ctrl+m] ASCII code 13 */
17660 				jx9MemObjStringAppend(pObj, "\r", sizeof(char));
17661 				break;
17662 			case 't':
17663 				/* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
17664 				jx9MemObjStringAppend(pObj, "\t", sizeof(char));
17665 				break;
17666 			case 'v':
17667 				/* Vertical tab(VT)[ctrl+k] ASCII code 11 */
17668 				jx9MemObjStringAppend(pObj, "\v", sizeof(char));
17669 				break;
17670 			case '\'':
17671 				/* Single quote */
17672 				jx9MemObjStringAppend(pObj, "'", sizeof(char));
17673 				break;
17674 			case '"':
17675 				/* Double quote */
17676 				jx9MemObjStringAppend(pObj, "\"", sizeof(char));
17677 				break;
17678 			case '0':
17679 				/* NUL byte */
17680 				jx9MemObjStringAppend(pObj, "\0", sizeof(char));
17681 				break;
17682 			case 'x':
17683 				if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
17684 					int c;
17685 					/* Hex digit */
17686 					c = SyHexToint(zIn[1]) << 4;
17687 					if( &zIn[2] < zEnd ){
17688 						c +=  SyHexToint(zIn[2]);
17689 					}
17690 					/* Output char */
17691 					jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
17692 					n += sizeof(char) * 2;
17693 				}else{
17694 					/* Output literal character  */
17695 					jx9MemObjStringAppend(pObj, "x", sizeof(char));
17696 				}
17697 				break;
17698 			case 'o':
17699 				if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
17700 					/* Octal digit stream */
17701 					int c;
17702 					c = 0;
17703 					zIn++;
17704 					for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
17705 						if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
17706 							break;
17707 						}
17708 						c = c * 8 + (zPtr[0] - '0');
17709 					}
17710 					if ( c > 0 ){
17711 						jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
17712 					}
17713 					n = (sxu32)(zPtr-zIn);
17714 				}else{
17715 					/* Output literal character  */
17716 					jx9MemObjStringAppend(pObj, "o", sizeof(char));
17717 				}
17718 				break;
17719 			default:
17720 				/* Output without a slash */
17721 				jx9MemObjStringAppend(pObj, zIn, sizeof(char));
17722 				break;
17723 			}
17724 			/* Advance the stream cursor */
17725 			zIn += n;
17726 			continue;
17727 		}
17728 		if( zIn[0] == '{' ){
17729 			/* Curly syntax */
17730 			const char *zExpr;
17731 			sxi32 iNest = 1;
17732 			zIn++;
17733 			zExpr = zIn;
17734 			/* Synchronize with the next closing curly braces */
17735 			while( zIn < zEnd ){
17736 				if( zIn[0] == '{' ){
17737 					/* Increment nesting level */
17738 					iNest++;
17739 				}else if(zIn[0] == '}' ){
17740 					/* Decrement nesting level */
17741 					iNest--;
17742 					if( iNest <= 0 ){
17743 						break;
17744 					}
17745 				}
17746 				zIn++;
17747 			}
17748 			/* Process the expression */
17749 			rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
17750 			if( rc == SXERR_ABORT ){
17751 				return SXERR_ABORT;
17752 			}
17753 			if( rc != SXERR_EMPTY ){
17754 				++iCons;
17755 			}
17756 			if( zIn < zEnd ){
17757 				/* Jump the trailing curly */
17758 				zIn++;
17759 			}
17760 		}else{
17761 			/* Simple syntax */
17762 			const char *zExpr = zIn;
17763 			/* Assemble variable name */
17764 			for(;;){
17765 				/* Jump leading dollars */
17766 				while( zIn < zEnd && zIn[0] == '$' ){
17767 					zIn++;
17768 				}
17769 				for(;;){
17770 					while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
17771 						zIn++;
17772 					}
17773 					if((unsigned char)zIn[0] >= 0xc0 ){
17774 						/* UTF-8 stream */
17775 						zIn++;
17776 						while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
17777 							zIn++;
17778 						}
17779 						continue;
17780 					}
17781 					break;
17782 				}
17783 				if( zIn >= zEnd ){
17784 					break;
17785 				}
17786 				if( zIn[0] == '[' ){
17787 					sxi32 iSquare = 1;
17788 					zIn++;
17789 					while( zIn < zEnd ){
17790 						if( zIn[0] == '[' ){
17791 							iSquare++;
17792 						}else if (zIn[0] == ']' ){
17793 							iSquare--;
17794 							if( iSquare <= 0 ){
17795 								break;
17796 							}
17797 						}
17798 						zIn++;
17799 					}
17800 					if( zIn < zEnd ){
17801 						zIn++;
17802 					}
17803 					break;
17804 				}else if( zIn[0] == '.' ){
17805 					/* Member access operator '.' */
17806 					zIn++;
17807 				}else{
17808 					break;
17809 				}
17810 			}
17811 			/* Process the expression */
17812 			rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
17813 			if( rc == SXERR_ABORT ){
17814 				return SXERR_ABORT;
17815 			}
17816 			if( rc != SXERR_EMPTY ){
17817 				++iCons;
17818 			}
17819 		}
17820 		/* Invalidate the previously used constant */
17821 		pObj = 0;
17822 	}/*for(;;)*/
17823 	if( iCons > 1 ){
17824 		/* Concatenate all compiled constants */
17825 		jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
17826 	}
17827 	/* Node successfully compiled */
17828 	return SXRET_OK;
17829 }
17830 /*
17831  * Compile a double quoted string.
17832  *  See the block-comment above for more information.
17833  */
jx9CompileString(jx9_gen_state * pGen,sxi32 iCompileFlag)17834 JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
17835 {
17836 	sxi32 rc;
17837 	rc = GenStateCompileString(&(*pGen));
17838 	SXUNUSED(iCompileFlag); /* cc warning */
17839 	/* Compilation result */
17840 	return rc;
17841 }
17842 /*
17843  * Compile a literal which is an identifier(name) for simple values.
17844  */
jx9CompileLiteral(jx9_gen_state * pGen,sxi32 iCompileFlag)17845 JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
17846 {
17847 	SyToken *pToken = pGen->pIn;
17848 	jx9_value *pObj;
17849 	SyString *pStr;
17850 	sxu32 nIdx;
17851 	/* Extract token value */
17852 	pStr = &pToken->sData;
17853 	/* Deal with the reserved literals [i.e: null, false, true, ...] first */
17854 	if( pStr->nByte == sizeof("NULL") - 1 ){
17855 		if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
17856 			/* NULL constant are always indexed at 0 */
17857 			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17858 			return SXRET_OK;
17859 		}else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
17860 			/* TRUE constant are always indexed at 1 */
17861 			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
17862 			return SXRET_OK;
17863 		}
17864 	}else if (pStr->nByte == sizeof("FALSE") - 1 &&
17865 		SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
17866 			/* FALSE constant are always indexed at 2 */
17867 			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
17868 			return SXRET_OK;
17869 	}else if(pStr->nByte == sizeof("__LINE__") - 1 &&
17870 		SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
17871 			/* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
17872 			pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17873 			if( pObj == 0 ){
17874 				SXUNUSED(iCompileFlag); /* cc warning */
17875 				return GenStateOutOfMem(pGen);
17876 			}
17877 			jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
17878 			/* Emit the load constant instruction */
17879 			jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17880 			return SXRET_OK;
17881 	}else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
17882 		SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
17883 			GenBlock *pBlock = pGen->pCurrent;
17884 			/* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
17885 			while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
17886 				/* Point to the upper block */
17887 				pBlock = pBlock->pParent;
17888 			}
17889 			if( pBlock == 0 ){
17890 				/* Called in the global scope, load NULL */
17891 				jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
17892 			}else{
17893 				/* Extract the target function/method */
17894 				jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
17895 				pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17896 				if( pObj == 0 ){
17897 					return GenStateOutOfMem(pGen);
17898 				}
17899 				jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
17900 				/* Emit the load constant instruction */
17901 				jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
17902 			}
17903 			return SXRET_OK;
17904 	}
17905 	/* Query literal table */
17906 	if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
17907 		jx9_value *pObj;
17908 		/* Unknown literal, install it in the literal table */
17909 		pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
17910 		if( pObj == 0 ){
17911 			return GenStateOutOfMem(pGen);
17912 		}
17913 		jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
17914 		GenStateInstallLiteral(&(*pGen), pObj, nIdx);
17915 	}
17916 	/* Emit the load constant instruction */
17917 	jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
17918 	/* Node successfully compiled */
17919 	return SXRET_OK;
17920 }
17921 /*
17922  * Compile an array entry whether it is a key or a value.
17923  */
GenStateCompileJSONEntry(jx9_gen_state * pGen,SyToken * pIn,SyToken * pEnd,sxi32 iFlags,sxi32 (* xValidator)(jx9_gen_state *,jx9_expr_node *))17924 static sxi32 GenStateCompileJSONEntry(
17925 	jx9_gen_state *pGen, /* Code generator state */
17926 	SyToken *pIn,        /* Token stream */
17927 	SyToken *pEnd,       /* End of the token stream */
17928 	sxi32 iFlags,        /* Compilation flags */
17929 	sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
17930 	)
17931 {
17932 	SyToken *pTmpIn, *pTmpEnd;
17933 	sxi32 rc;
17934 	/* Swap token stream */
17935 	SWAP_DELIMITER(pGen, pIn, pEnd);
17936 	/* Compile the expression*/
17937 	rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
17938 	/* Restore token stream */
17939 	RE_SWAP_DELIMITER(pGen);
17940 	return rc;
17941 }
17942 /*
17943  * Compile a Jx9 JSON Array.
17944  */
jx9CompileJsonArray(jx9_gen_state * pGen,sxi32 iCompileFlag)17945 JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
17946 {
17947 	sxi32 nPair = 0;
17948 	SyToken *pCur;
17949 	sxi32 rc;
17950 
17951 	pGen->pIn++; /* Jump the open square bracket '['*/
17952 	pGen->pEnd--;
17953 	SXUNUSED(iCompileFlag); /* cc warning */
17954 	for(;;){
17955 		/* Jump leading commas */
17956 		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
17957 			pGen->pIn++;
17958 		}
17959 		pCur = pGen->pIn;
17960 		if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
17961 			/* No more entry to process */
17962 			break;
17963 		}
17964 		/* Compile entry */
17965 		rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
17966 		if( rc == SXERR_ABORT ){
17967 			return SXERR_ABORT;
17968 		}
17969 		nPair++;
17970 	}
17971 	/* Emit the load map instruction */
17972 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
17973 	/* Node successfully compiled */
17974 	return SXRET_OK;
17975 }
17976 /*
17977  * Node validator for a given JSON key.
17978  */
GenStateJSONObjectKeyNodeValidator(jx9_gen_state * pGen,jx9_expr_node * pRoot)17979 static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
17980 {
17981 	sxi32 rc = SXRET_OK;
17982 	if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString
17983 		&& pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
17984 		/* Unexpected expression */
17985 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0,
17986 			"JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
17987 		if( rc != SXERR_ABORT ){
17988 			rc = SXERR_INVALID;
17989 		}
17990 	}
17991 	return rc;
17992 }
17993 /*
17994  * Compile a Jx9 JSON Object
17995  */
jx9CompileJsonObject(jx9_gen_state * pGen,sxi32 iCompileFlag)17996 JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
17997 {
17998 	SyToken *pKey, *pCur;
17999 	sxi32 nPair = 0;
18000 	sxi32 rc;
18001 
18002 	pGen->pIn++; /* Jump the open querly braces '{'*/
18003 	pGen->pEnd--;
18004 	SXUNUSED(iCompileFlag); /* cc warning */
18005 	for(;;){
18006 		/* Jump leading commas */
18007 		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
18008 			pGen->pIn++;
18009 		}
18010 		pCur = pGen->pIn;
18011 		if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
18012 			/* No more entry to process */
18013 			break;
18014 		}
18015 		/* Compile the key */
18016 		pKey = pCur;
18017 		while( pCur < pGen->pIn ){
18018 			if( pCur->nType & JX9_TK_COLON /*':'*/  ){
18019 				break;
18020 			}
18021 			pCur++;
18022 		}
18023 		rc = SXERR_EMPTY;
18024         if( (pCur->nType & JX9_TK_COLON) == 0 ){
18025             rc = jx9GenCompileError(&(*pGen), E_ABORT, pCur->nLine, "JSON Object: Missing colon string \":\"");
18026             if( rc == SXERR_ABORT ){
18027                 return SXERR_ABORT;
18028             }
18029             return SXRET_OK;
18030         }
18031 
18032 		if( pCur < pGen->pIn ){
18033 			if( &pCur[1] >= pGen->pIn ){
18034 				/* Missing value */
18035 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
18036 				if( rc == SXERR_ABORT ){
18037 					return SXERR_ABORT;
18038 				}
18039 				return SXRET_OK;
18040 			}
18041 			/* Compile the expression holding the key */
18042 			rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
18043 				EXPR_FLAG_RDONLY_LOAD                /* Do not create the variable if inexistant */,
18044 				GenStateJSONObjectKeyNodeValidator   /* Node validator callback */
18045 				);
18046 			if( rc == SXERR_ABORT ){
18047 				return SXERR_ABORT;
18048 			}
18049 			pCur++; /* Jump the double colon ':'  */
18050 		}else if( pKey == pCur ){
18051 			/* Key is omitted, emit an error */
18052 			jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
18053 			pCur++; /* Jump the double colon ':'  */
18054 		}else{
18055 			/* Reset back the cursor and point to the entry value */
18056 			pCur = pKey;
18057 		}
18058 		/* Compile indice value */
18059 		rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
18060 		if( rc == SXERR_ABORT ){
18061 			return SXERR_ABORT;
18062 		}
18063 		nPair++;
18064 	}
18065 	/* Emit the load map instruction */
18066 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
18067 	/* Node successfully compiled */
18068 	return SXRET_OK;
18069 }
18070 /*
18071  * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
18072  * construct.
18073  */
jx9CompileLangConstruct(jx9_gen_state * pGen,sxi32 iCompileFlag)18074 JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
18075 {
18076 	SyString *pName;
18077 	sxu32 nKeyID;
18078 	sxi32 rc;
18079 	/* Name of the language construct [i.e: print, die...]*/
18080 	pName = &pGen->pIn->sData;
18081 	nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
18082 	pGen->pIn++; /* Jump the language construct keyword */
18083 	if( nKeyID == JX9_TKWRD_PRINT ){
18084 		SyToken *pTmp, *pNext = 0;
18085 		/* Compile arguments one after one */
18086 		pTmp = pGen->pEnd;
18087 		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
18088 		while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
18089 			if( pGen->pIn < pNext ){
18090 				pGen->pEnd = pNext;
18091 				rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
18092 				if( rc == SXERR_ABORT ){
18093 					return SXERR_ABORT;
18094 				}
18095 				if( rc != SXERR_EMPTY ){
18096 					/* Ticket 1433-008: Optimization #1: Consume input directly
18097 					 * without the overhead of a function call.
18098 					 * This is a very powerful optimization that improve
18099 					 * performance greatly.
18100 					 */
18101 					jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
18102 				}
18103 			}
18104 			/* Jump trailing commas */
18105 			while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
18106 				pNext++;
18107 			}
18108 			pGen->pIn = pNext;
18109 		}
18110 		/* Restore token stream */
18111 		pGen->pEnd = pTmp;
18112 	}else{
18113 		sxi32 nArg = 0;
18114 		sxu32 nIdx = 0;
18115 		rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
18116 		if( rc == SXERR_ABORT ){
18117 			return SXERR_ABORT;
18118 		}else if(rc != SXERR_EMPTY ){
18119 			nArg = 1;
18120 		}
18121 		if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
18122 			jx9_value *pObj;
18123 			/* Emit the call instruction */
18124 			pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
18125 			if( pObj == 0 ){
18126 				SXUNUSED(iCompileFlag); /* cc warning */
18127 				return GenStateOutOfMem(pGen);
18128 			}
18129 			jx9MemObjInitFromString(pGen->pVm, pObj, pName);
18130 			/* Install in the literal table */
18131 			GenStateInstallLiteral(&(*pGen), pObj, nIdx);
18132 		}
18133 		/* Emit the call instruction */
18134 		jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
18135 		jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
18136 	}
18137 	/* Node successfully compiled */
18138 	return SXRET_OK;
18139 }
18140 /*
18141  * Compile a node holding a variable declaration.
18142  * According to the J9X language reference
18143  *  Variables in JX9 are represented by a dollar sign followed by the name of the variable.
18144  *  The variable name is case-sensitive.
18145  *  Variable names follow the same rules as other labels in JX9. A valid variable name
18146  *  starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
18147  *  numbers, or underscores.
18148  *  By default, variables are always assigned by value unless the target value is a JSON
18149  *  array or a JSON object which is passed by reference.
18150  */
jx9CompileVariable(jx9_gen_state * pGen,sxi32 iCompileFlag)18151 JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
18152 {
18153 	sxu32 nLine = pGen->pIn->nLine;
18154 	SyHashEntry *pEntry;
18155 	SyString *pName;
18156 	char *zName = 0;
18157 	sxi32 iP1;
18158 	void *p3;
18159 	sxi32 rc;
18160 
18161 	pGen->pIn++; /* Jump the dollar sign '$' */
18162 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
18163 		/* Invalid variable name */
18164 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
18165 		if( rc == SXERR_ABORT ){
18166 			/* Error count limit reached, abort immediately */
18167 			return SXERR_ABORT;
18168 		}
18169 		return SXRET_OK;
18170 	}
18171 	/* Extract variable name */
18172 	pName = &pGen->pIn->sData;
18173 	/* Advance the stream cursor */
18174 	pGen->pIn++;
18175 	pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
18176 	if( pEntry == 0 ){
18177 		/* Duplicate name */
18178 		zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
18179 		if( zName == 0 ){
18180 			return GenStateOutOfMem(pGen);
18181 		}
18182 		/* Install in the hashtable */
18183 		SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
18184 	}else{
18185 		/* Name already available */
18186 		zName = (char *)pEntry->pUserData;
18187 	}
18188 	p3 = (void *)zName;
18189 	iP1 = 0;
18190 	if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
18191 		if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
18192 			/* Read-only load.In other words do not create the variable if inexistant */
18193 			iP1 = 1;
18194 		}
18195 	}
18196 	/* Emit the load instruction */
18197 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
18198 	/* Node successfully compiled */
18199 	return SXRET_OK;
18200 }
18201 /* Forward declaration */
18202 static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
18203 /*
18204  * Compile an annoynmous function or a closure.
18205  * According to the JX9 language reference
18206  *  Anonymous functions, also known as closures, allow the creation of functions
18207  *  which have no specified name. They are most useful as the value of callback
18208  *  parameters, but they have many other uses. Closures can also be used as
18209  *  the values of variables; Assigning a closure to a variable uses the same
18210  *  syntax as any other assignment, including the trailing semicolon:
18211  *  Example Anonymous function variable assignment example
18212  * $greet = function($name)
18213  * {
18214  *    printf("Hello %s\r\n", $name);
18215  * };
18216  * $greet('World');
18217  * $greet('JX9');
18218  * Note that the implementation of annoynmous function and closure under
18219  * JX9 is completely different from the one used by the  engine.
18220  */
jx9CompileAnnonFunc(jx9_gen_state * pGen,sxi32 iCompileFlag)18221 JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
18222 {
18223 	jx9_vm_func *pAnnonFunc; /* Annonymous function body */
18224 	char zName[512];         /* Unique lambda name */
18225 	static int iCnt = 1;     /* There is no worry about thread-safety here, because only
18226 							  * one thread is allowed to compile the script.
18227 						      */
18228 	jx9_value *pObj;
18229 	SyString sName;
18230 	sxu32 nIdx;
18231 	sxu32 nLen;
18232 	sxi32 rc;
18233 
18234 	pGen->pIn++; /* Jump the 'function' keyword */
18235 	if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
18236 		pGen->pIn++;
18237 	}
18238 	/* Reserve a constant for the lambda */
18239 	pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
18240 	if( pObj == 0 ){
18241 		GenStateOutOfMem(pGen);
18242 		SXUNUSED(iCompileFlag); /* cc warning */
18243 		return SXERR_ABORT;
18244 	}
18245 	/* Generate a unique name */
18246 	nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
18247 	/* Make sure the generated name is unique */
18248 	while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
18249 		nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
18250 	}
18251 	SyStringInitFromBuf(&sName, zName, nLen);
18252 	jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
18253 	/* Compile the lambda body */
18254 	rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
18255 	if( rc == SXERR_ABORT ){
18256 		return SXERR_ABORT;
18257 	}
18258 	/* Emit the load constant instruction */
18259 	jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
18260 	/* Node successfully compiled */
18261 	return SXRET_OK;
18262 }
18263 /*
18264  * Compile the 'continue' statement.
18265  * According to the JX9 language reference
18266  *  continue is used within looping structures to skip the rest of the current loop iteration
18267  *  and continue execution at the condition evaluation and then the beginning of the next
18268  *  iteration.
18269  *  Note: Note that in JX9 the switch statement is considered a looping structure for
18270  *  the purposes of continue.
18271  *  continue accepts an optional numeric argument which tells it how many levels
18272  *  of enclosing loops it should skip to the end of.
18273  *  Note:
18274  *   continue 0; and continue 1; is the same as running continue;.
18275  */
jx9CompileContinue(jx9_gen_state * pGen)18276 static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
18277 {
18278 	GenBlock *pLoop; /* Target loop */
18279 	sxi32 iLevel;    /* How many nesting loop to skip */
18280 	sxu32 nLine;
18281 	sxi32 rc;
18282 	nLine = pGen->pIn->nLine;
18283 	iLevel = 0;
18284 	/* Jump the 'continue' keyword */
18285 	pGen->pIn++;
18286 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
18287 		/* optional numeric argument which tells us how many levels
18288 		 * of enclosing loops we should skip to the end of.
18289 		 */
18290 		iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
18291 		if( iLevel < 2 ){
18292 			iLevel = 0;
18293 		}
18294 		pGen->pIn++; /* Jump the optional numeric argument */
18295 	}
18296 	/* Point to the target loop */
18297 	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
18298 	if( pLoop == 0 ){
18299 		/* Illegal continue */
18300 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
18301 		if( rc == SXERR_ABORT ){
18302 			/* Error count limit reached, abort immediately */
18303 			return SXERR_ABORT;
18304 		}
18305 	}else{
18306 		sxu32 nInstrIdx = 0;
18307 		/* Emit the unconditional jump to the beginning of the target loop */
18308 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
18309 		if( pLoop->bPostContinue == TRUE ){
18310 			JumpFixup sJumpFix;
18311 			/* Post-continue */
18312 			sJumpFix.nJumpType = JX9_OP_JMP;
18313 			sJumpFix.nInstrIdx = nInstrIdx;
18314 			SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
18315 		}
18316 	}
18317 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18318 		/* Not so fatal, emit a warning only */
18319 		jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
18320 	}
18321 	/* Statement successfully compiled */
18322 	return SXRET_OK;
18323 }
18324 /*
18325  * Compile the 'break' statement.
18326  * According to the JX9 language reference
18327  *  break ends execution of the current for, foreach, while, do-while or switch
18328  *  structure.
18329  *  break accepts an optional numeric argument which tells it how many nested
18330  *  enclosing structures are to be broken out of.
18331  */
jx9CompileBreak(jx9_gen_state * pGen)18332 static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
18333 {
18334 	GenBlock *pLoop; /* Target loop */
18335 	sxi32 iLevel;    /* How many nesting loop to skip */
18336 	sxu32 nLine;
18337 	sxi32 rc;
18338 	nLine = pGen->pIn->nLine;
18339 	iLevel = 0;
18340 	/* Jump the 'break' keyword */
18341 	pGen->pIn++;
18342 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
18343 		/* optional numeric argument which tells us how many levels
18344 		 * of enclosing loops we should skip to the end of.
18345 		 */
18346 		iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
18347 		if( iLevel < 2 ){
18348 			iLevel = 0;
18349 		}
18350 		pGen->pIn++; /* Jump the optional numeric argument */
18351 	}
18352 	/* Extract the target loop */
18353 	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
18354 	if( pLoop == 0 ){
18355 		/* Illegal break */
18356 		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
18357 		if( rc == SXERR_ABORT ){
18358 			/* Error count limit reached, abort immediately */
18359 			return SXERR_ABORT;
18360 		}
18361 	}else{
18362 		sxu32 nInstrIdx;
18363 		rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
18364 		if( rc == SXRET_OK ){
18365 			/* Fix the jump later when the jump destination is resolved */
18366 			GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
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 'break' statement");
18372 	}
18373 	/* Statement successfully compiled */
18374 	return SXRET_OK;
18375 }
18376 /* Forward declaration */
18377 static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
18378 /*
18379  * Compile a JX9 block.
18380  * A block is simply one or more JX9 statements and expressions to compile
18381  * optionally delimited by braces {}.
18382  * Return SXRET_OK on success. Any other return value indicates failure
18383  * and this function takes care of generating the appropriate error
18384  * message.
18385  */
jx9CompileBlock(jx9_gen_state * pGen)18386 static sxi32 jx9CompileBlock(
18387 	jx9_gen_state *pGen /* Code generator state */
18388 	)
18389 {
18390 	sxi32 rc;
18391 	if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
18392 		sxu32 nLine = pGen->pIn->nLine;
18393 		rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
18394 		if( rc != SXRET_OK ){
18395 			return SXERR_ABORT;
18396 		}
18397 		pGen->pIn++;
18398 		/* Compile until we hit the closing braces '}' */
18399 		for(;;){
18400 			if( pGen->pIn >= pGen->pEnd ){
18401 				/* No more token to process. Missing closing braces */
18402 				jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
18403 				break;
18404 			}
18405 			if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
18406 				/* Closing braces found, break immediately*/
18407 				pGen->pIn++;
18408 				break;
18409 			}
18410 			/* Compile a single statement */
18411 			rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
18412 			if( rc == SXERR_ABORT ){
18413 				return SXERR_ABORT;
18414 			}
18415 		}
18416 		GenStateLeaveBlock(&(*pGen), 0);
18417 	}else{
18418 		/* Compile a single statement */
18419 		rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
18420 		if( rc == SXERR_ABORT ){
18421 			return SXERR_ABORT;
18422 		}
18423 	}
18424 	/* Jump trailing semi-colons ';' */
18425 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
18426 		pGen->pIn++;
18427 	}
18428 	return SXRET_OK;
18429 }
18430 /*
18431  * Compile the gentle 'while' statement.
18432  * According to the JX9 language reference
18433  *  while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
18434  *  The basic form of a while statement is:
18435  *  while (expr)
18436  *   statement
18437  *  The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
18438  *  repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
18439  *  is checked each time at the beginning of the loop, so even if this value changes during
18440  *  the execution of the nested statement(s), execution will not stop until the end of the iteration
18441  *  (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
18442  *  expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
18443  *  Like with the if statement, you can group multiple statements within the same while loop by surrounding
18444  *  a group of statements with curly braces, or by using the alternate syntax:
18445  *  while (expr):
18446  *    statement
18447  *   endwhile;
18448  */
jx9CompileWhile(jx9_gen_state * pGen)18449 static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
18450 {
18451 	GenBlock *pWhileBlock = 0;
18452 	SyToken *pTmp, *pEnd = 0;
18453 	sxu32 nFalseJump;
18454 	sxu32 nLine;
18455 	sxi32 rc;
18456 	nLine = pGen->pIn->nLine;
18457 	/* Jump the 'while' keyword */
18458 	pGen->pIn++;
18459 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18460 		/* Syntax error */
18461 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
18462 		if( rc == SXERR_ABORT ){
18463 			/* Error count limit reached, abort immediately */
18464 			return SXERR_ABORT;
18465 		}
18466 		goto Synchronize;
18467 	}
18468 	/* Jump the left parenthesis '(' */
18469 	pGen->pIn++;
18470 	/* Create the loop block */
18471 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
18472 	if( rc != SXRET_OK ){
18473 		return SXERR_ABORT;
18474 	}
18475 	/* Delimit the condition */
18476 	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18477 	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18478 		/* Empty expression */
18479 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
18480 		if( rc == SXERR_ABORT ){
18481 			/* Error count limit reached, abort immediately */
18482 			return SXERR_ABORT;
18483 		}
18484 	}
18485 	/* Swap token streams */
18486 	pTmp = pGen->pEnd;
18487 	pGen->pEnd = pEnd;
18488 	/* Compile the expression */
18489 	rc = jx9CompileExpr(&(*pGen), 0, 0);
18490 	if( rc == SXERR_ABORT ){
18491 		/* Expression handler request an operation abort [i.e: Out-of-memory] */
18492 		return SXERR_ABORT;
18493 	}
18494 	/* Update token stream */
18495 	while(pGen->pIn < pEnd ){
18496 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
18497 		if( rc == SXERR_ABORT ){
18498 			return SXERR_ABORT;
18499 		}
18500 		pGen->pIn++;
18501 	}
18502 	/* Synchronize pointers */
18503 	pGen->pIn  = &pEnd[1];
18504 	pGen->pEnd = pTmp;
18505 	/* Emit the false jump */
18506 	jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
18507 	/* Save the instruction index so we can fix it later when the jump destination is resolved */
18508 	GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
18509 	/* Compile the loop body */
18510 	rc = jx9CompileBlock(&(*pGen));
18511 	if( rc == SXERR_ABORT ){
18512 		return SXERR_ABORT;
18513 	}
18514 	/* Emit the unconditional jump to the start of the loop */
18515 	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
18516 	/* Fix all jumps now the destination is resolved */
18517 	GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
18518 	/* Release the loop block */
18519 	GenStateLeaveBlock(pGen, 0);
18520 	/* Statement successfully compiled */
18521 	return SXRET_OK;
18522 Synchronize:
18523 	/* Synchronize with the first semi-colon ';' so we can avoid
18524 	 * compiling this erroneous block.
18525 	 */
18526 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
18527 		pGen->pIn++;
18528 	}
18529 	return SXRET_OK;
18530 }
18531 /*
18532  * Compile the complex and powerful 'for' statement.
18533  * According to the JX9 language reference
18534  *  for loops are the most complex loops in JX9. They behave like their C counterparts.
18535  *  The syntax of a for loop is:
18536  *  for (expr1; expr2; expr3)
18537  *   statement
18538  *  The first expression (expr1) is evaluated (executed) once unconditionally at
18539  *  the beginning of the loop.
18540  *  In the beginning of each iteration, expr2 is evaluated. If it evaluates to
18541  *  TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
18542  *  to FALSE, the execution of the loop ends.
18543  *  At the end of each iteration, expr3 is evaluated (executed).
18544  *  Each of the expressions can be empty or contain multiple expressions separated by commas.
18545  *  In expr2, all expressions separated by a comma are evaluated but the result is taken
18546  *  from the last part. expr2 being empty means the loop should be run indefinitely
18547  *  (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
18548  *  think, since often you'd want to end the loop using a conditional break statement instead
18549  *  of using the for truth expression.
18550  */
jx9CompileFor(jx9_gen_state * pGen)18551 static sxi32 jx9CompileFor(jx9_gen_state *pGen)
18552 {
18553 	SyToken *pTmp, *pPostStart, *pEnd = 0;
18554 	GenBlock *pForBlock = 0;
18555 	sxu32 nFalseJump;
18556 	sxu32 nLine;
18557 	sxi32 rc;
18558 	nLine = pGen->pIn->nLine;
18559 	/* Jump the 'for' keyword */
18560 	pGen->pIn++;
18561 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18562 		/* Syntax error */
18563 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
18564 		if( rc == SXERR_ABORT ){
18565 			/* Error count limit reached, abort immediately */
18566 			return SXERR_ABORT;
18567 		}
18568 		return SXRET_OK;
18569 	}
18570 	/* Jump the left parenthesis '(' */
18571 	pGen->pIn++;
18572 	/* Delimit the init-expr;condition;post-expr */
18573 	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18574 	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18575 		/* Empty expression */
18576 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
18577 		if( rc == SXERR_ABORT ){
18578 			/* Error count limit reached, abort immediately */
18579 			return SXERR_ABORT;
18580 		}
18581 		/* Synchronize */
18582 		pGen->pIn = pEnd;
18583 		if( pGen->pIn < pGen->pEnd ){
18584 			pGen->pIn++;
18585 		}
18586 		return SXRET_OK;
18587 	}
18588 	/* Swap token streams */
18589 	pTmp = pGen->pEnd;
18590 	pGen->pEnd = pEnd;
18591 	/* Compile initialization expressions if available */
18592 	rc = jx9CompileExpr(&(*pGen), 0, 0);
18593 	/* Pop operand lvalues */
18594 	if( rc == SXERR_ABORT ){
18595 		/* Expression handler request an operation abort [i.e: Out-of-memory] */
18596 		return SXERR_ABORT;
18597 	}else if( rc != SXERR_EMPTY ){
18598 		jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
18599 	}
18600 	if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18601 		/* Syntax error */
18602 		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
18603 			"for: Expected ';' after initialization expressions");
18604 		if( rc == SXERR_ABORT ){
18605 			/* Error count limit reached, abort immediately */
18606 			return SXERR_ABORT;
18607 		}
18608 		return SXRET_OK;
18609 	}
18610 	/* Jump the trailing ';' */
18611 	pGen->pIn++;
18612 	/* Create the loop block */
18613 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
18614 	if( rc != SXRET_OK ){
18615 		return SXERR_ABORT;
18616 	}
18617 	/* Deffer continue jumps */
18618 	pForBlock->bPostContinue = TRUE;
18619 	/* Compile the condition */
18620 	rc = jx9CompileExpr(&(*pGen), 0, 0);
18621 	if( rc == SXERR_ABORT ){
18622 		/* Expression handler request an operation abort [i.e: Out-of-memory] */
18623 		return SXERR_ABORT;
18624 	}else if( rc != SXERR_EMPTY ){
18625 		/* Emit the false jump */
18626 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
18627 		/* Save the instruction index so we can fix it later when the jump destination is resolved */
18628 		GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
18629 	}
18630 	if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
18631 		/* Syntax error */
18632 		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
18633 			"for: Expected ';' after conditionals expressions");
18634 		if( rc == SXERR_ABORT ){
18635 			/* Error count limit reached, abort immediately */
18636 			return SXERR_ABORT;
18637 		}
18638 		return SXRET_OK;
18639 	}
18640 	/* Jump the trailing ';' */
18641 	pGen->pIn++;
18642 	/* Save the post condition stream */
18643 	pPostStart = pGen->pIn;
18644 	/* Compile the loop body */
18645 	pGen->pIn  = &pEnd[1]; /* Jump the trailing parenthesis ')' */
18646 	pGen->pEnd = pTmp;
18647 	rc = jx9CompileBlock(&(*pGen));
18648 	if( rc == SXERR_ABORT ){
18649 		return SXERR_ABORT;
18650 	}
18651 	/* Fix post-continue jumps */
18652 	if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
18653 		JumpFixup *aPost;
18654 		VmInstr *pInstr;
18655 		sxu32 nJumpDest;
18656 		sxu32 n;
18657 		aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
18658 		nJumpDest = jx9VmInstrLength(pGen->pVm);
18659 		for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
18660 			pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
18661 			if( pInstr ){
18662 				/* Fix jump */
18663 				pInstr->iP2 = nJumpDest;
18664 			}
18665 		}
18666 	}
18667 	/* compile the post-expressions if available */
18668 	while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
18669 		pPostStart++;
18670 	}
18671 	if( pPostStart < pEnd ){
18672 		SyToken *pTmpIn, *pTmpEnd;
18673 		SWAP_DELIMITER(pGen, pPostStart, pEnd);
18674 		rc = jx9CompileExpr(&(*pGen), 0, 0);
18675 		if( pGen->pIn < pGen->pEnd ){
18676 			/* Syntax error */
18677 			rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
18678 			if( rc == SXERR_ABORT ){
18679 				/* Error count limit reached, abort immediately */
18680 				return SXERR_ABORT;
18681 			}
18682 			return SXRET_OK;
18683 		}
18684 		RE_SWAP_DELIMITER(pGen);
18685 		if( rc == SXERR_ABORT ){
18686 			/* Expression handler request an operation abort [i.e: Out-of-memory] */
18687 			return SXERR_ABORT;
18688 		}else if( rc != SXERR_EMPTY){
18689 			/* Pop operand lvalue */
18690 			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
18691 		}
18692 	}
18693 	/* Emit the unconditional jump to the start of the loop */
18694 	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
18695 	/* Fix all jumps now the destination is resolved */
18696 	GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
18697 	/* Release the loop block */
18698 	GenStateLeaveBlock(pGen, 0);
18699 	/* Statement successfully compiled */
18700 	return SXRET_OK;
18701 }
18702 /* Expression tree validator callback used by the 'foreach' statement.
18703  * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
18704  * are allowed.
18705  */
GenStateForEachNodeValidator(jx9_gen_state * pGen,jx9_expr_node * pRoot)18706 static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
18707 {
18708 	sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
18709 	if( pRoot->xCode != jx9CompileVariable ){
18710 		/* Unexpected expression */
18711 		rc = jx9GenCompileError(&(*pGen),
18712 			E_ERROR,
18713 			pRoot->pStart? pRoot->pStart->nLine : 0,
18714 			"foreach: Expecting a variable name"
18715 			);
18716 		if( rc != SXERR_ABORT ){
18717 			rc = SXERR_INVALID;
18718 		}
18719 	}
18720 	return rc;
18721 }
18722 /*
18723  * Compile the 'foreach' statement.
18724  * According to the JX9 language reference
18725  *  The foreach construct simply gives an easy way to iterate over arrays. foreach works
18726  *  only on arrays (and objects), and will issue an error when you try to use it on a variable
18727  *  with a different data type or an uninitialized variable. There are two syntaxes; the second
18728  *  is a minor but useful extension of the first:
18729  *  foreach (json_array_json_object as $value)
18730  *    statement
18731  *  foreach (json_array_json_objec as $key,$value)
18732  *   statement
18733  *  The first form loops over the array given by array_expression. On each loop, the value
18734  *  of the current element is assigned to $value and the internal array pointer is advanced
18735  *  by one (so on the next loop, you'll be looking at the next element).
18736  *  The second form does the same thing, except that the current element's key will be assigned
18737  *  to the variable $key on each loop.
18738  *  Note:
18739  *  When foreach first starts executing, the internal array pointer is automatically reset to the
18740  *  first element of the array. This means that you do not need to call reset() before a foreach loop.
18741  */
jx9CompileForeach(jx9_gen_state * pGen)18742 static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
18743 {
18744 	SyToken *pCur, *pTmp, *pEnd = 0;
18745 	GenBlock *pForeachBlock = 0;
18746 	jx9_foreach_info *pInfo;
18747 	sxu32 nFalseJump;
18748 	VmInstr *pInstr;
18749 	sxu32 nLine;
18750 	sxi32 rc;
18751 	nLine = pGen->pIn->nLine;
18752 	/* Jump the 'foreach' keyword */
18753 	pGen->pIn++;
18754 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
18755 		/* Syntax error */
18756 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
18757 		if( rc == SXERR_ABORT ){
18758 			/* Error count limit reached, abort immediately */
18759 			return SXERR_ABORT;
18760 		}
18761 		goto Synchronize;
18762 	}
18763 	/* Jump the left parenthesis '(' */
18764 	pGen->pIn++;
18765 	/* Create the loop block */
18766 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
18767 	if( rc != SXRET_OK ){
18768 		return SXERR_ABORT;
18769 	}
18770 	/* Delimit the expression */
18771 	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18772 	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
18773 		/* Empty expression */
18774 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
18775 		if( rc == SXERR_ABORT ){
18776 			/* Error count limit reached, abort immediately */
18777 			return SXERR_ABORT;
18778 		}
18779 		/* Synchronize */
18780 		pGen->pIn = pEnd;
18781 		if( pGen->pIn < pGen->pEnd ){
18782 			pGen->pIn++;
18783 		}
18784 		return SXRET_OK;
18785 	}
18786 	/* Compile the array expression */
18787 	pCur = pGen->pIn;
18788 	while( pCur < pEnd ){
18789 		if( pCur->nType & JX9_TK_KEYWORD ){
18790 			sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
18791 			if( nKeywrd == JX9_TKWRD_AS ){
18792 				/* Break with the first 'as' found */
18793 				break;
18794 			}
18795 		}
18796 		/* Advance the stream cursor */
18797 		pCur++;
18798 	}
18799 	if( pCur <= pGen->pIn ){
18800 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
18801 			"foreach: Missing array/object expression");
18802 		if( rc == SXERR_ABORT ){
18803 			/* Don't worry about freeing memory, everything will be released shortly */
18804 			return SXERR_ABORT;
18805 		}
18806 		goto Synchronize;
18807 	}
18808 	/* Swap token streams */
18809 	pTmp = pGen->pEnd;
18810 	pGen->pEnd = pCur;
18811 	rc = jx9CompileExpr(&(*pGen), 0, 0);
18812 	if( rc == SXERR_ABORT ){
18813 		/* Expression handler request an operation abort [i.e: Out-of-memory] */
18814 		return SXERR_ABORT;
18815 	}
18816 	/* Update token stream */
18817 	while(pGen->pIn < pCur ){
18818 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
18819 		if( rc == SXERR_ABORT ){
18820 			/* Don't worry about freeing memory, everything will be released shortly */
18821 			return SXERR_ABORT;
18822 		}
18823 		pGen->pIn++;
18824 	}
18825 	pCur++; /* Jump the 'as' keyword */
18826 	pGen->pIn = pCur;
18827 	if( pGen->pIn >= pEnd ){
18828 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
18829 		if( rc == SXERR_ABORT ){
18830 			return SXERR_ABORT;
18831 		}
18832 	}
18833 	/* Create the foreach context */
18834 	pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
18835 	if( pInfo == 0 ){
18836 		jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
18837 		return SXERR_ABORT;
18838 	}
18839 	/* Zero the structure */
18840 	SyZero(pInfo, sizeof(jx9_foreach_info));
18841 	/* Initialize structure fields */
18842 	SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
18843 	/* Check if we have a key field */
18844 	while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
18845 		pCur++;
18846 	}
18847 	if( pCur < pEnd ){
18848 		/* Compile the expression holding the key name */
18849 		if( pGen->pIn >= pCur ){
18850 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
18851 			if( rc == SXERR_ABORT ){
18852 				/* Don't worry about freeing memory, everything will be released shortly */
18853 				return SXERR_ABORT;
18854 			}
18855 		}else{
18856 			pGen->pEnd = pCur;
18857 			rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
18858 			if( rc == SXERR_ABORT ){
18859 				/* Don't worry about freeing memory, everything will be released shortly */
18860 				return SXERR_ABORT;
18861 			}
18862 			pInstr = jx9VmPopInstr(pGen->pVm);
18863 			if( pInstr->p3 ){
18864 				/* Record key name */
18865 				SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
18866 			}
18867 			pInfo->iFlags |= JX9_4EACH_STEP_KEY;
18868 		}
18869 		pGen->pIn = &pCur[1]; /* Jump the arrow */
18870 	}
18871 	pGen->pEnd = pEnd;
18872 	if( pGen->pIn >= pEnd ){
18873 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
18874 		if( rc == SXERR_ABORT ){
18875 			/* Don't worry about freeing memory, everything will be released shortly */
18876 			return SXERR_ABORT;
18877 		}
18878 		goto Synchronize;
18879 	}
18880 	/* Compile the expression holding the value name */
18881 	rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
18882 	if( rc == SXERR_ABORT ){
18883 		/* Don't worry about freeing memory, everything will be released shortly */
18884 		return SXERR_ABORT;
18885 	}
18886 	pInstr = jx9VmPopInstr(pGen->pVm);
18887 	if( pInstr->p3 ){
18888 		/* Record value name */
18889 		SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
18890 	}
18891 	/* Emit the 'FOREACH_INIT' instruction */
18892 	jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
18893 	/* Save the instruction index so we can fix it later when the jump destination is resolved */
18894 	GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
18895 	/* Record the first instruction to execute */
18896 	pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
18897 	/* Emit the FOREACH_STEP instruction */
18898     jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
18899 	/* Save the instruction index so we can fix it later when the jump destination is resolved */
18900 	GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
18901 	/* Compile the loop body */
18902 	pGen->pIn = &pEnd[1];
18903 	pGen->pEnd = pTmp;
18904 	rc = jx9CompileBlock(&(*pGen));
18905 	if( rc == SXERR_ABORT ){
18906 		/* Don't worry about freeing memory, everything will be released shortly */
18907 		return SXERR_ABORT;
18908 	}
18909 	/* Emit the unconditional jump to the start of the loop */
18910 	jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
18911 	/* Fix all jumps now the destination is resolved */
18912 	GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
18913 	/* Release the loop block */
18914 	GenStateLeaveBlock(pGen, 0);
18915 	/* Statement successfully compiled */
18916 	return SXRET_OK;
18917 Synchronize:
18918 	/* Synchronize with the first semi-colon ';' so we can avoid
18919 	 * compiling this erroneous block.
18920 	 */
18921 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
18922 		pGen->pIn++;
18923 	}
18924 	return SXRET_OK;
18925 }
18926 /*
18927  * Compile the infamous if/elseif/else if/else statements.
18928  * According to the JX9 language reference
18929  *  The if construct is one of the most important features of many languages JX9 included.
18930  *  It allows for conditional execution of code fragments. JX9 features an if structure
18931  *  that is similar to that of C:
18932  *  if (expr)
18933  *   statement
18934  *  else construct:
18935  *   Often you'd want to execute a statement if a certain condition is met, and a different
18936  *   statement if the condition is not met. This is what else is for. else extends an if statement
18937  *   to execute a statement in case the expression in the if statement evaluates to FALSE.
18938  *   For example, the following code would display a is greater than b if $a is greater than
18939  *   $b, and a is NOT greater than b otherwise.
18940  *   The else statement is only executed if the if expression evaluated to FALSE, and if there
18941  *   were any elseif expressions - only if they evaluated to FALSE as well
18942  *  elseif
18943  *   elseif, as its name suggests, is a combination of if and else. Like else, it extends
18944  *   an if statement to execute a different statement in case the original if expression evaluates
18945  *   to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
18946  *   conditional expression evaluates to TRUE. For example, the following code would display a is bigger
18947  *   than b, a equal to b or a is smaller than b:
18948  *    if ($a > $b) {
18949  *     print "a is bigger than b";
18950  *    } elseif ($a == $b) {
18951  *     print "a is equal to b";
18952  *    } else {
18953  *     print "a is smaller than b";
18954  *    }
18955  */
jx9CompileIf(jx9_gen_state * pGen)18956 static sxi32 jx9CompileIf(jx9_gen_state *pGen)
18957 {
18958 	SyToken *pToken, *pTmp, *pEnd = 0;
18959 	GenBlock *pCondBlock = 0;
18960 	sxu32 nJumpIdx;
18961 	sxu32 nKeyID;
18962 	sxi32 rc;
18963 	/* Jump the 'if' keyword */
18964 	pGen->pIn++;
18965 	pToken = pGen->pIn;
18966 	/* Create the conditional block */
18967 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
18968 	if( rc != SXRET_OK ){
18969 		return SXERR_ABORT;
18970 	}
18971 	/* Process as many [if/else if/elseif/else] blocks as we can */
18972 	for(;;){
18973 		if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
18974 			/* Syntax error */
18975 			if( pToken >= pGen->pEnd ){
18976 				pToken--;
18977 			}
18978 			rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
18979 			if( rc == SXERR_ABORT ){
18980 				/* Error count limit reached, abort immediately */
18981 				return SXERR_ABORT;
18982 			}
18983 			goto Synchronize;
18984 		}
18985 		/* Jump the left parenthesis '(' */
18986 		pToken++;
18987 		/* Delimit the condition */
18988 		jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
18989 		if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
18990 			/* Syntax error */
18991 			if( pToken >= pGen->pEnd ){
18992 				pToken--;
18993 			}
18994 			rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
18995 			if( rc == SXERR_ABORT ){
18996 				/* Error count limit reached, abort immediately */
18997 				return SXERR_ABORT;
18998 			}
18999 			goto Synchronize;
19000 		}
19001 		/* Swap token streams */
19002 		SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
19003 		/* Compile the condition */
19004 		rc = jx9CompileExpr(&(*pGen), 0, 0);
19005 		/* Update token stream */
19006 		while(pGen->pIn < pEnd ){
19007 			jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
19008 			pGen->pIn++;
19009 		}
19010 		pGen->pIn  = &pEnd[1];
19011 		pGen->pEnd = pTmp;
19012 		if( rc == SXERR_ABORT ){
19013 			/* Expression handler request an operation abort [i.e: Out-of-memory] */
19014 			return SXERR_ABORT;
19015 		}
19016 		/* Emit the false jump */
19017 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
19018 		/* Save the instruction index so we can fix it later when the jump destination is resolved */
19019 		GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
19020 		/* Compile the body */
19021 		rc = jx9CompileBlock(&(*pGen));
19022 		if( rc == SXERR_ABORT ){
19023 			return SXERR_ABORT;
19024 		}
19025 		if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19026 			break;
19027 		}
19028 		/* Ensure that the keyword ID is 'else if' or 'else' */
19029 		nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
19030 		if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
19031 			break;
19032 		}
19033 		/* Emit the unconditional jump */
19034 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
19035 		/* Save the instruction index so we can fix it later when the jump destination is resolved */
19036 		GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
19037 		if( nKeyID & JX9_TKWRD_ELSE ){
19038 			pToken = &pGen->pIn[1];
19039 			if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
19040 				SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
19041 					break;
19042 			}
19043 			pGen->pIn++; /* Jump the 'else' keyword */
19044 		}
19045 		pGen->pIn++; /* Jump the 'elseif/if' keyword */
19046 		/* Synchronize cursors */
19047 		pToken = pGen->pIn;
19048 		/* Fix the false jump */
19049 		GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
19050 	} /* For(;;) */
19051 	/* Fix the false jump */
19052 	GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
19053 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
19054 		(SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
19055 			/* Compile the else block */
19056 			pGen->pIn++;
19057 			rc = jx9CompileBlock(&(*pGen));
19058 			if( rc == SXERR_ABORT ){
19059 
19060 				return SXERR_ABORT;
19061 			}
19062 	}
19063 	nJumpIdx = jx9VmInstrLength(pGen->pVm);
19064 	/* Fix all unconditional jumps now the destination is resolved */
19065 	GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
19066 	/* Release the conditional block */
19067 	GenStateLeaveBlock(pGen, 0);
19068 	/* Statement successfully compiled */
19069 	return SXRET_OK;
19070 Synchronize:
19071 	/* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
19072 	 */
19073 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
19074 		pGen->pIn++;
19075 	}
19076 	return SXRET_OK;
19077 }
19078 /*
19079  * Compile the return statement.
19080  * According to the JX9 language reference
19081  *  If called from within a function, the return() statement immediately ends execution
19082  *  of the current function, and returns its argument as the value of the function call.
19083  *  return() will also end the execution of an eval() statement or script file.
19084  *  If called from the global scope, then execution of the current script file is ended.
19085  *  If the current script file was include()ed or require()ed, then control is passed back
19086  *  to the calling file. Furthermore, if the current script file was include()ed, then the value
19087  *  given to return() will be returned as the value of the include() call. If return() is called
19088  *  from within the main script file, then script execution end.
19089  *  Note that since return() is a language construct and not a function, the parentheses
19090  *  surrounding its arguments are not required. It is common to leave them out, and you actually
19091  *  should do so as JX9 has less work to do in this case.
19092  *  Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
19093  */
jx9CompileReturn(jx9_gen_state * pGen)19094 static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
19095 {
19096 	sxi32 nRet = 0; /* TRUE if there is a return value */
19097 	sxi32 rc;
19098 	/* Jump the 'return' keyword */
19099 	pGen->pIn++;
19100 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19101 		/* Compile the expression */
19102 		rc = jx9CompileExpr(&(*pGen), 0, 0);
19103 		if( rc == SXERR_ABORT ){
19104 			return SXERR_ABORT;
19105 		}else if(rc != SXERR_EMPTY ){
19106 			nRet = 1;
19107 		}
19108 	}
19109 	/* Emit the done instruction */
19110 	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
19111 	return SXRET_OK;
19112 }
19113 /*
19114  * Compile the die/exit language construct.
19115  * The role of these constructs is to terminate execution of the script.
19116  * Shutdown functions will always be executed even if exit() is called.
19117  */
jx9CompileHalt(jx9_gen_state * pGen)19118 static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
19119 {
19120 	sxi32 nExpr = 0;
19121 	sxi32 rc;
19122 	/* Jump the die/exit keyword */
19123 	pGen->pIn++;
19124 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19125 		/* Compile the expression */
19126 		rc = jx9CompileExpr(&(*pGen), 0, 0);
19127 		if( rc == SXERR_ABORT ){
19128 			return SXERR_ABORT;
19129 		}else if(rc != SXERR_EMPTY ){
19130 			nExpr = 1;
19131 		}
19132 	}
19133 	/* Emit the HALT instruction */
19134 	jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
19135 	return SXRET_OK;
19136 }
19137 /*
19138  * Compile the static statement.
19139  * According to the JX9 language reference
19140  *  Another important feature of variable scoping is the static variable.
19141  *  A static variable exists only in a local function scope, but it does not lose its value
19142  *  when program execution leaves this scope.
19143  *  Static variables also provide one way to deal with recursive functions.
19144  */
jx9CompileStatic(jx9_gen_state * pGen)19145 static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
19146 {
19147 	jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
19148 	jx9_vm_func *pFunc;             /* Enclosing function */
19149 	GenBlock *pBlock;
19150 	SyString *pName;
19151 	char *zDup;
19152 	sxu32 nLine;
19153 	sxi32 rc;
19154 	/* Jump the static keyword */
19155 	nLine = pGen->pIn->nLine;
19156 	pGen->pIn++;
19157 	/* Extract the enclosing function if any */
19158 	pBlock = pGen->pCurrent;
19159 	while( pBlock ){
19160 		if( pBlock->iFlags & GEN_BLOCK_FUNC){
19161 			break;
19162 		}
19163 		/* Point to the upper block */
19164 		pBlock = pBlock->pParent;
19165 	}
19166 	if( pBlock == 0 ){
19167 		/* Static statement, called outside of a function body, treat it as a simple variable. */
19168 		if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
19169 			rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
19170 			if( rc == SXERR_ABORT ){
19171 				return SXERR_ABORT;
19172 			}
19173 			goto Synchronize;
19174 		}
19175 		/* Compile the expression holding the variable */
19176 		rc = jx9CompileExpr(&(*pGen), 0, 0);
19177 		if( rc == SXERR_ABORT ){
19178 			return SXERR_ABORT;
19179 		}else if( rc != SXERR_EMPTY ){
19180 			/* Emit the POP instruction */
19181 			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
19182 		}
19183 		return SXRET_OK;
19184 	}
19185 	pFunc = (jx9_vm_func *)pBlock->pUserData;
19186 	/* Make sure we are dealing with a valid statement */
19187 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
19188 		(pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19189 			rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
19190 			if( rc == SXERR_ABORT ){
19191 				return SXERR_ABORT;
19192 			}
19193 			goto Synchronize;
19194 	}
19195 	pGen->pIn++;
19196 	/* Extract variable name */
19197 	pName = &pGen->pIn->sData;
19198 	pGen->pIn++; /* Jump the var name */
19199 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
19200 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
19201 		goto Synchronize;
19202 	}
19203 	/* Initialize the structure describing the static variable */
19204 	SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19205 	sStatic.nIdx = SXU32_HIGH; /* Not yet created */
19206 	/* Duplicate variable name */
19207 	zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
19208 	if( zDup == 0 ){
19209 		jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
19210 		return SXERR_ABORT;
19211 	}
19212 	SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
19213 	/* Check if we have an expression to compile */
19214 	if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
19215 		SySet *pInstrContainer;
19216 		pGen->pIn++; /* Jump the equal '=' sign */
19217 		/* Swap bytecode container */
19218 		pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19219 		jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
19220 		/* Compile the expression */
19221 		rc = jx9CompileExpr(&(*pGen), 0, 0);
19222 		/* Emit the done instruction */
19223 		jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19224 		/* Restore default bytecode container */
19225 		jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19226 	}
19227 	/* Finally save the compiled static variable in the appropriate container */
19228 	SySetPut(&pFunc->aStatic, (const void *)&sStatic);
19229 	return SXRET_OK;
19230 Synchronize:
19231 	/* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
19232 	 * statement.
19233 	 */
19234 	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ==  0 ){
19235 		pGen->pIn++;
19236 	}
19237 	return SXRET_OK;
19238 }
19239 /*
19240  * Compile the 'const' statement.
19241  * According to the JX9 language reference
19242  *  A constant is an identifier (name) for a simple value. As the name suggests, that value
19243  *  cannot change during the execution of the script (except for magic constants, which aren't actually constants).
19244  *  A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
19245  *  The name of a constant follows the same rules as any label in JX9. A valid constant name starts
19246  *  with a letter or underscore, followed by any number of letters, numbers, or underscores.
19247  *  As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
19248  *  Syntax
19249  *  You can define a constant by using the define()-function or by using the const keyword outside
19250  *  a object definition. Once a constant is defined, it can never be changed or undefined.
19251  *  You can get the value of a constant by simply specifying its name. Unlike with variables
19252  *  you should not prepend a constant with a $. You can also use the function constant() to read
19253  *  a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
19254  *  to get a list of all defined constants.
19255  */
jx9CompileConstant(jx9_gen_state * pGen)19256 static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
19257 {
19258 	SySet *pConsCode, *pInstrContainer;
19259 	sxu32 nLine = pGen->pIn->nLine;
19260 	SyString *pName;
19261 	sxi32 rc;
19262 	pGen->pIn++; /* Jump the 'const' keyword */
19263 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19264 		/* Invalid constant name */
19265 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
19266 		if( rc == SXERR_ABORT ){
19267 			/* Error count limit reached, abort immediately */
19268 			return SXERR_ABORT;
19269 		}
19270 		goto Synchronize;
19271 	}
19272 	/* Peek constant name */
19273 	pName = &pGen->pIn->sData;
19274 	/* Make sure the constant name isn't reserved */
19275 	if( GenStateIsReservedID(pName) ){
19276 		/* Reserved constant */
19277 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
19278 		if( rc == SXERR_ABORT ){
19279 			/* Error count limit reached, abort immediately */
19280 			return SXERR_ABORT;
19281 		}
19282 		goto Synchronize;
19283 	}
19284 	pGen->pIn++;
19285 	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
19286 		/* Invalid statement*/
19287 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
19288 		if( rc == SXERR_ABORT ){
19289 			/* Error count limit reached, abort immediately */
19290 			return SXERR_ABORT;
19291 		}
19292 		goto Synchronize;
19293 	}
19294 	pGen->pIn++; /*Jump the equal sign */
19295 	/* Allocate a new constant value container */
19296 	pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
19297 	if( pConsCode == 0 ){
19298 		return GenStateOutOfMem(pGen);
19299 	}
19300 	SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19301 	/* Swap bytecode container */
19302 	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19303 	jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
19304 	/* Compile constant value */
19305 	rc = jx9CompileExpr(&(*pGen), 0, 0);
19306 	/* Emit the done instruction */
19307 	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19308 	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19309 	if( rc == SXERR_ABORT ){
19310 		/* Don't worry about freeing memory, everything will be released shortly */
19311 		return SXERR_ABORT;
19312 	}
19313 	SySetSetUserData(pConsCode, pGen->pVm);
19314 	/* Register the constant */
19315 	rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
19316 	if( rc != SXRET_OK ){
19317 		SySetRelease(pConsCode);
19318 		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
19319 	}
19320 	return SXRET_OK;
19321 Synchronize:
19322 	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
19323 	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19324 		pGen->pIn++;
19325 	}
19326 	return SXRET_OK;
19327 }
19328 /*
19329  * Compile the uplink construct.
19330  * According to the JX9 language reference
19331  *  In JX9 global variables must be declared uplink inside a function if they are going
19332  *  to be used in that function.
19333  *  Example #1 Using global
19334  *   $a = 1;
19335  *   $b = 2;
19336  *   function Sum()
19337  *   {
19338  *    uplink $a, $b;
19339  *    $b = $a + $b;
19340  *   }
19341  *   Sum();
19342  *   print $b;
19343  *  ?>
19344  *  The above script will output 3. By declaring $a and $b global within the function
19345  *  all references to either variable will refer to the global version. There is no limit
19346  *  to the number of global variables that can be manipulated by a function.
19347  */
jx9CompileUplink(jx9_gen_state * pGen)19348 static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
19349 {
19350 	SyToken *pTmp, *pNext = 0;
19351 	sxi32 nExpr;
19352 	sxi32 rc;
19353 	/* Jump the 'uplink' keyword */
19354 	pGen->pIn++;
19355 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
19356 		/* Nothing to process */
19357 		return SXRET_OK;
19358 	}
19359 	pTmp = pGen->pEnd;
19360 	nExpr = 0;
19361 	while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
19362 		if( pGen->pIn < pNext ){
19363 			pGen->pEnd = pNext;
19364 			if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
19365 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
19366 				if( rc == SXERR_ABORT ){
19367 					return SXERR_ABORT;
19368 				}
19369 			}else{
19370 				pGen->pIn++;
19371 				if( pGen->pIn >= pGen->pEnd ){
19372 					/* Emit a warning */
19373 					jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
19374 				}else{
19375 					rc = jx9CompileExpr(&(*pGen), 0, 0);
19376 					if( rc == SXERR_ABORT ){
19377 						return SXERR_ABORT;
19378 					}else if(rc != SXERR_EMPTY ){
19379 						nExpr++;
19380 					}
19381 				}
19382 			}
19383 		}
19384 		/* Next expression in the stream */
19385 		pGen->pIn = pNext;
19386 		/* Jump trailing commas */
19387 		while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
19388 			pGen->pIn++;
19389 		}
19390 	}
19391 	/* Restore token stream */
19392 	pGen->pEnd = pTmp;
19393 	if( nExpr > 0 ){
19394 		/* Emit the uplink instruction */
19395 		jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
19396 	}
19397 	return SXRET_OK;
19398 }
19399 /*
19400  * Compile a switch block.
19401  *  (See block-comment below for more information)
19402  */
GenStateCompileSwitchBlock(jx9_gen_state * pGen,sxu32 * pBlockStart)19403 static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
19404 {
19405 	sxi32 rc = SXRET_OK;
19406 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
19407 		/* Unexpected token */
19408 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
19409 		if( rc == SXERR_ABORT ){
19410 			return SXERR_ABORT;
19411 		}
19412 		pGen->pIn++;
19413 	}
19414 	pGen->pIn++;
19415 	/* First instruction to execute in this block. */
19416 	*pBlockStart = jx9VmInstrLength(pGen->pVm);
19417 	/* Compile the block until we hit a case/default/endswitch keyword
19418 	 * or the '}' token */
19419 	for(;;){
19420 		if( pGen->pIn >= pGen->pEnd ){
19421 			/* No more input to process */
19422 			break;
19423 		}
19424 		rc = SXRET_OK;
19425 		if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19426 			if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
19427 				rc = SXERR_EOF;
19428 				break;
19429 			}
19430 		}else{
19431 			sxi32 nKwrd;
19432 			/* Extract the keyword */
19433 			nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
19434 			if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
19435 				break;
19436 			}
19437 		}
19438 		/* Compile block */
19439 		rc = jx9CompileBlock(&(*pGen));
19440 		if( rc == SXERR_ABORT ){
19441 			return SXERR_ABORT;
19442 		}
19443 	}
19444 	return rc;
19445 }
19446 /*
19447  * Compile a case eXpression.
19448  *  (See block-comment below for more information)
19449  */
GenStateCompileCaseExpr(jx9_gen_state * pGen,jx9_case_expr * pExpr)19450 static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
19451 {
19452 	SySet *pInstrContainer;
19453 	SyToken *pEnd, *pTmp;
19454 	sxi32 iNest = 0;
19455 	sxi32 rc;
19456 	/* Delimit the expression */
19457 	pEnd = pGen->pIn;
19458 	while( pEnd < pGen->pEnd ){
19459 		if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
19460 			/* Increment nesting level */
19461 			iNest++;
19462 		}else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
19463 			/* Decrement nesting level */
19464 			iNest--;
19465 		}else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
19466 			break;
19467 		}
19468 		pEnd++;
19469 	}
19470 	if( pGen->pIn >= pEnd ){
19471 		rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
19472 		if( rc == SXERR_ABORT ){
19473 			/* Error count limit reached, abort immediately */
19474 			return SXERR_ABORT;
19475 		}
19476 	}
19477 	/* Swap token stream */
19478 	pTmp = pGen->pEnd;
19479 	pGen->pEnd = pEnd;
19480 	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19481 	jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
19482 	rc = jx9CompileExpr(&(*pGen), 0, 0);
19483 	/* Emit the done instruction */
19484 	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19485 	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19486 	/* Update token stream */
19487 	pGen->pIn  = pEnd;
19488 	pGen->pEnd = pTmp;
19489 	if( rc == SXERR_ABORT ){
19490 		return SXERR_ABORT;
19491 	}
19492 	return SXRET_OK;
19493 }
19494 /*
19495  * Compile the smart switch statement.
19496  * According to the JX9 language reference manual
19497  *  The switch statement is similar to a series of IF statements on the same expression.
19498  *  In many occasions, you may want to compare the same variable (or expression) with many
19499  *  different values, and execute a different piece of code depending on which value it equals to.
19500  *  This is exactly what the switch statement is for.
19501  *  Note: Note that unlike some other languages, the continue statement applies to switch and acts
19502  *  similar to break. If you have a switch inside a loop and wish to continue to the next iteration
19503  *  of the outer loop, use continue 2.
19504  *  Note that switch/case does loose comparision.
19505  *  It is important to understand how the switch statement is executed in order to avoid mistakes.
19506  *  The switch statement executes line by line (actually, statement by statement).
19507  *  In the beginning, no code is executed. Only when a case statement is found with a value that
19508  *  matches the value of the switch expression does JX9 begin to execute the statements.
19509  *  JX9 continues to execute the statements until the end of the switch block, or the first time
19510  *  it sees a break statement. If you don't write a break statement at the end of a case's statement list.
19511  *  In a switch statement, the condition is evaluated only once and the result is compared to each
19512  *  case statement. In an elseif statement, the condition is evaluated again. If your condition
19513  *  is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
19514  *  The statement list for a case can also be empty, which simply passes control into the statement
19515  *  list for the next case.
19516  *  The case expression may be any expression that evaluates to a simple type, that is, integer
19517  *  or floating-point numbers and strings.
19518  */
jx9CompileSwitch(jx9_gen_state * pGen)19519 static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
19520 {
19521 	GenBlock *pSwitchBlock;
19522 	SyToken *pTmp, *pEnd;
19523 	jx9_switch *pSwitch;
19524 	sxu32 nLine;
19525 	sxi32 rc;
19526 	nLine = pGen->pIn->nLine;
19527 	/* Jump the 'switch' keyword */
19528 	pGen->pIn++;
19529 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
19530 		/* Syntax error */
19531 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
19532 		if( rc == SXERR_ABORT ){
19533 			/* Error count limit reached, abort immediately */
19534 			return SXERR_ABORT;
19535 		}
19536 		goto Synchronize;
19537 	}
19538 	/* Jump the left parenthesis '(' */
19539 	pGen->pIn++;
19540 	pEnd = 0; /* cc warning */
19541 	/* Create the loop block */
19542 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH,
19543 		jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
19544 	if( rc != SXRET_OK ){
19545 		return SXERR_ABORT;
19546 	}
19547 	/* Delimit the condition */
19548 	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
19549 	if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
19550 		/* Empty expression */
19551 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
19552 		if( rc == SXERR_ABORT ){
19553 			/* Error count limit reached, abort immediately */
19554 			return SXERR_ABORT;
19555 		}
19556 	}
19557 	/* Swap token streams */
19558 	pTmp = pGen->pEnd;
19559 	pGen->pEnd = pEnd;
19560 	/* Compile the expression */
19561 	rc = jx9CompileExpr(&(*pGen), 0, 0);
19562 	if( rc == SXERR_ABORT ){
19563 		/* Expression handler request an operation abort [i.e: Out-of-memory] */
19564 		return SXERR_ABORT;
19565 	}
19566 	/* Update token stream */
19567 	while(pGen->pIn < pEnd ){
19568 		rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
19569 			"Switch: Unexpected token '%z'", &pGen->pIn->sData);
19570 		if( rc == SXERR_ABORT ){
19571 			return SXERR_ABORT;
19572 		}
19573 		pGen->pIn++;
19574 	}
19575 	pGen->pIn  = &pEnd[1];
19576 	pGen->pEnd = pTmp;
19577 	if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
19578 		(pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
19579 			pTmp = pGen->pIn;
19580 			if( pTmp >= pGen->pEnd ){
19581 				pTmp--;
19582 			}
19583 			/* Unexpected token */
19584 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
19585 			if( rc == SXERR_ABORT ){
19586 				return SXERR_ABORT;
19587 			}
19588 			goto Synchronize;
19589 	}
19590 	pGen->pIn++; /* Jump the leading curly braces/colons */
19591 	/* Create the switch blocks container */
19592 	pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
19593 	if( pSwitch == 0 ){
19594 		/* Abort compilation */
19595 		return GenStateOutOfMem(pGen);
19596 	}
19597 	/* Zero the structure */
19598 	SyZero(pSwitch, sizeof(jx9_switch));
19599 	/* Initialize fields */
19600 	SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
19601 	/* Emit the switch instruction */
19602 	jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
19603 	/* Compile case blocks */
19604 	for(;;){
19605 		sxu32 nKwrd;
19606 		if( pGen->pIn >= pGen->pEnd ){
19607 			/* No more input to process */
19608 			break;
19609 		}
19610 		if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
19611 			if(  (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
19612 				/* Unexpected token */
19613 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
19614 					&pGen->pIn->sData);
19615 				if( rc == SXERR_ABORT ){
19616 					return SXERR_ABORT;
19617 				}
19618 				/* FALL THROUGH */
19619 			}
19620 			/* Block compiled */
19621 			break;
19622 		}
19623 		/* Extract the keyword */
19624 		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
19625 		if( nKwrd == JX9_TKWRD_DEFAULT ){
19626 			/*
19627 			 * Accroding to the JX9 language reference manual
19628 			 *  A special case is the default case. This case matches anything
19629 			 *  that wasn't matched by the other cases.
19630 			 */
19631 			if( pSwitch->nDefault > 0 ){
19632 				/* Default case already compiled */
19633 				rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
19634 				if( rc == SXERR_ABORT ){
19635 					return SXERR_ABORT;
19636 				}
19637 			}
19638 			pGen->pIn++; /* Jump the 'default' keyword */
19639 			/* Compile the default block */
19640 			rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
19641 			if( rc == SXERR_ABORT){
19642 				return SXERR_ABORT;
19643 			}else if( rc == SXERR_EOF ){
19644 				break;
19645 			}
19646 		}else if( nKwrd == JX9_TKWRD_CASE ){
19647 			jx9_case_expr sCase;
19648 			/* Standard case block */
19649 			pGen->pIn++; /* Jump the 'case' keyword */
19650 			/* initialize the structure */
19651 			SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19652 			/* Compile the case expression */
19653 			rc = GenStateCompileCaseExpr(pGen, &sCase);
19654 			if( rc == SXERR_ABORT ){
19655 				return SXERR_ABORT;
19656 			}
19657 			/* Compile the case block */
19658 			rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
19659 			/* Insert in the switch container */
19660 			SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
19661 			if( rc == SXERR_ABORT){
19662 				return SXERR_ABORT;
19663 			}else if( rc == SXERR_EOF ){
19664 				break;
19665 			}
19666 		}else{
19667 			/* Unexpected token */
19668 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
19669 				&pGen->pIn->sData);
19670 			if( rc == SXERR_ABORT ){
19671 				return SXERR_ABORT;
19672 			}
19673 			break;
19674 		}
19675 	}
19676 	/* Fix all jumps now the destination is resolved */
19677 	pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
19678 	GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
19679 	/* Release the loop block */
19680 	GenStateLeaveBlock(pGen, 0);
19681 	if( pGen->pIn < pGen->pEnd ){
19682 		/* Jump the trailing curly braces */
19683 		pGen->pIn++;
19684 	}
19685 	/* Statement successfully compiled */
19686 	return SXRET_OK;
19687 Synchronize:
19688 	/* Synchronize with the first semi-colon */
19689 	while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
19690 		pGen->pIn++;
19691 	}
19692 	return SXRET_OK;
19693 }
19694 /*
19695  * Process default argument values. That is, a function may define C++-style default value
19696  * as follows:
19697  * function makecoffee($type = "cappuccino")
19698  * {
19699  *   return "Making a cup of $type.\n";
19700  * }
19701  * Some features:
19702  *  1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
19703  *      functions, array member, ..]
19704  * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
19705  *      Example:
19706  *           function a(string $a){} function b(int $a, string $c, float $d){}
19707  * 3 -) Function overloading!!
19708  *      Example:
19709  *      function foo($a) {
19710  *   	  return $a.JX9_EOL;
19711  *	    }
19712  *	    function foo($a, $b) {
19713  *   	  return $a + $b;
19714  *	    }
19715  *	    print foo(5); // Prints "5"
19716  *	    print foo(5, 2); // Prints "7"
19717  *      // Same arg
19718  *	   function foo(string $a)
19719  *	   {
19720  *	     print "a is a string\n";
19721  *	     dump($a);
19722  *	   }
19723  *	  function foo(int $a)
19724  *	  {
19725  *	    print "a is integer\n";
19726  *	    dump($a);
19727  *	  }
19728  *	  function foo(array $a)
19729  *	  {
19730  * 	    print "a is an array\n";
19731  * 	    dump($a);
19732  *	  }
19733  *	  foo('This is a great feature'); // a is a string [first foo]
19734  *	  foo(52); // a is integer [second foo]
19735  *    foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
19736  * Please refer to the official documentation for more information on the powerful extension
19737  * introduced by the JX9 engine.
19738  */
GenStateProcessArgValue(jx9_gen_state * pGen,jx9_vm_func_arg * pArg,SyToken * pIn,SyToken * pEnd)19739 static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
19740 {
19741 	SyToken *pTmpIn, *pTmpEnd;
19742 	SySet *pInstrContainer;
19743 	sxi32 rc;
19744 	/* Swap token stream */
19745 	SWAP_DELIMITER(pGen, pIn, pEnd);
19746 	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19747 	jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
19748 	/* Compile the expression holding the argument value */
19749 	rc = jx9CompileExpr(&(*pGen), 0, 0);
19750 	/* Emit the done instruction */
19751 	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
19752 	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19753 	RE_SWAP_DELIMITER(pGen);
19754 	if( rc == SXERR_ABORT ){
19755 		return SXERR_ABORT;
19756 	}
19757 	return SXRET_OK;
19758 }
19759 /*
19760  * Collect function arguments one after one.
19761  * According to the JX9 language reference manual.
19762  * Information may be passed to functions via the argument list, which is a comma-delimited
19763  * list of expressions.
19764  * JX9 supports passing arguments by value (the default), passing by reference
19765  * and default argument values. Variable-length argument lists are also supported,
19766  * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
19767  * for more information.
19768  * Example #1 Passing arrays to functions
19769  * <?jx9
19770  * function takes_array($input)
19771  * {
19772  *    print "$input[0] + $input[1] = ", $input[0]+$input[1];
19773  * }
19774  * ?>
19775  * Making arguments be passed by reference
19776  * By default, function arguments are passed by value (so that if the value of the argument
19777  * within the function is changed, it does not get changed outside of the function).
19778  * To allow a function to modify its arguments, they must be passed by reference.
19779  * To have an argument to a function always passed by reference, prepend an ampersand (&)
19780  * to the argument name in the function definition:
19781  * Example #2 Passing function parameters by reference
19782  * <?jx9
19783  * function add_some_extra(&$string)
19784  * {
19785  *   $string .= 'and something extra.';
19786  * }
19787  * $str = 'This is a string, ';
19788  * add_some_extra($str);
19789  * print $str;    // outputs 'This is a string, and something extra.'
19790  * ?>
19791  *
19792  * JX9 have introduced powerful extension including full type hinting, function overloading
19793  * complex agrument values.Please refer to the official documentation for more information
19794  * on these extension.
19795  */
GenStateCollectFuncArgs(jx9_vm_func * pFunc,jx9_gen_state * pGen,SyToken * pEnd)19796 static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
19797 {
19798 	jx9_vm_func_arg sArg; /* Current processed argument */
19799 	SyToken *pCur, *pIn;  /* Token stream */
19800 	SyBlob sSig;         /* Function signature */
19801 	char *zDup;          /* Copy of argument name */
19802 	sxi32 rc;
19803 
19804 	pIn = pGen->pIn;
19805 	pCur = 0;
19806 	SyBlobInit(&sSig, &pGen->pVm->sAllocator);
19807 	/* Process arguments one after one */
19808 	for(;;){
19809 		if( pIn >= pEnd ){
19810 			/* No more arguments to process */
19811 			break;
19812 		}
19813 		SyZero(&sArg, sizeof(jx9_vm_func_arg));
19814 		SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
19815 		if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
19816 			if( pIn->nType & JX9_TK_KEYWORD ){
19817 				sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
19818 				if( nKey & JX9_TKWRD_BOOL ){
19819 					sArg.nType = MEMOBJ_BOOL;
19820 				}else if( nKey & JX9_TKWRD_INT ){
19821 					sArg.nType = MEMOBJ_INT;
19822 				}else if( nKey & JX9_TKWRD_STRING ){
19823 					sArg.nType = MEMOBJ_STRING;
19824 				}else if( nKey & JX9_TKWRD_FLOAT ){
19825 					sArg.nType = MEMOBJ_REAL;
19826 				}else{
19827 					jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
19828 						"Invalid argument type '%z', Automatic cast will not be performed",
19829 						&pIn->sData);
19830 				}
19831 			}
19832 			pIn++;
19833 		}
19834 		if( pIn >= pEnd ){
19835 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
19836 			return rc;
19837 		}
19838 		if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
19839 			/* Invalid argument */
19840 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
19841 			return rc;
19842 		}
19843 		pIn++; /* Jump the dollar sign */
19844 		/* Copy argument name */
19845 		zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
19846 		if( zDup == 0 ){
19847 			return GenStateOutOfMem(pGen);
19848 		}
19849 		SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
19850 		pIn++;
19851 		if( pIn < pEnd ){
19852 			if( pIn->nType & JX9_TK_EQUAL ){
19853 				SyToken *pDefend;
19854 				sxi32 iNest = 0;
19855 				pIn++; /* Jump the equal sign */
19856 				pDefend = pIn;
19857 				/* Process the default value associated with this argument */
19858 				while( pDefend < pEnd ){
19859 					if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
19860 						break;
19861 					}
19862 					if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
19863 						/* Increment nesting level */
19864 						iNest++;
19865 					}else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
19866 						/* Decrement nesting level */
19867 						iNest--;
19868 					}
19869 					pDefend++;
19870 				}
19871 				if( pIn >= pDefend ){
19872 					rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
19873 					return rc;
19874 				}
19875 				/* Process default value */
19876 				rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
19877 				if( rc != SXRET_OK ){
19878 					return rc;
19879 				}
19880 				/* Point beyond the default value */
19881 				pIn = pDefend;
19882 			}
19883 			if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
19884 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
19885 				return rc;
19886 			}
19887 			pIn++; /* Jump the trailing comma */
19888 		}
19889 		/* Append argument signature */
19890 		if( sArg.nType > 0 ){
19891 			int c;
19892 			c = 'n'; /* cc warning */
19893 			/* Type leading character */
19894 			switch(sArg.nType){
19895 				case MEMOBJ_HASHMAP:
19896 					/* Hashmap aka 'array' */
19897 					c = 'h';
19898 					break;
19899 				case MEMOBJ_INT:
19900 					/* Integer */
19901 					c = 'i';
19902 					break;
19903 				case MEMOBJ_BOOL:
19904 					/* Bool */
19905 					c = 'b';
19906 					break;
19907 				case MEMOBJ_REAL:
19908 					/* Float */
19909 					c = 'f';
19910 					break;
19911 				case MEMOBJ_STRING:
19912 					/* String */
19913 					c = 's';
19914 					break;
19915 				default:
19916 					break;
19917 				}
19918 				SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
19919 		}
19920 		/* Save in the argument set */
19921 		SySetPut(&pFunc->aArgs, (const void *)&sArg);
19922 	}
19923 	if( SyBlobLength(&sSig) > 0 ){
19924 		/* Save function signature */
19925 		SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
19926 	}
19927 	return SXRET_OK;
19928 }
19929 /*
19930  * Compile function [i.e: standard function, annonymous function or closure ] body.
19931  * Return SXRET_OK on success. Any other return value indicates failure
19932  * and this routine takes care of generating the appropriate error message.
19933  */
GenStateCompileFuncBody(jx9_gen_state * pGen,jx9_vm_func * pFunc)19934 static sxi32 GenStateCompileFuncBody(
19935 	jx9_gen_state *pGen,  /* Code generator state */
19936 	jx9_vm_func *pFunc    /* Function state */
19937 	)
19938 {
19939 	SySet *pInstrContainer; /* Instruction container */
19940 	GenBlock *pBlock;
19941 	sxi32 rc;
19942 	/* Attach the new function */
19943 	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
19944 	if( rc != SXRET_OK ){
19945 		return GenStateOutOfMem(pGen);
19946 	}
19947 	/* Swap bytecode containers */
19948 	pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
19949 	jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
19950 	/* Compile the body */
19951 	jx9CompileBlock(&(*pGen));
19952 	/* Emit the final return if not yet done */
19953 	jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
19954 	/* Restore the default container */
19955 	jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
19956 	/* Leave function block */
19957 	GenStateLeaveBlock(&(*pGen), 0);
19958 	if( rc == SXERR_ABORT ){
19959 		/* Don't worry about freeing memory, everything will be released shortly */
19960 		return SXERR_ABORT;
19961 	}
19962 	/* All done, function body compiled */
19963 	return SXRET_OK;
19964 }
19965 /*
19966  * Compile a JX9 function whether is a Standard or Annonymous function.
19967  * According to the JX9 language reference manual.
19968  *  Function names follow the same rules as other labels in JX9. A valid function name
19969  *  starts with a letter or underscore, followed by any number of letters, numbers, or
19970  *  underscores. As a regular expression, it would be expressed thus:
19971  *     [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
19972  *  Functions need not be defined before they are referenced.
19973  *  All functions and objectes in JX9 have the global scope - they can be called outside
19974  *  a function even if they were defined inside and vice versa.
19975  *  It is possible to call recursive functions in JX9. However avoid recursive function/method
19976  *  calls with over 32-64 recursion levels.
19977  *
19978  * JX9 have introduced powerful extension including full type hinting, function overloading,
19979  * complex agrument values and more. Please refer to the official documentation for more information
19980  * on these extension.
19981  */
GenStateCompileFunc(jx9_gen_state * pGen,SyString * pName,sxi32 iFlags,jx9_vm_func ** ppFunc)19982 static sxi32 GenStateCompileFunc(
19983 	jx9_gen_state *pGen, /* Code generator state */
19984 	SyString *pName,     /* Function name. NULL otherwise */
19985 	sxi32 iFlags,        /* Control flags */
19986 	jx9_vm_func **ppFunc /* OUT: function state */
19987 	)
19988 {
19989 	jx9_vm_func *pFunc;
19990 	SyToken *pEnd;
19991 	sxu32 nLine;
19992 	char *zName;
19993 	sxi32 rc;
19994 	/* Extract line number */
19995 	nLine = pGen->pIn->nLine;
19996 	/* Jump the left parenthesis '(' */
19997 	pGen->pIn++;
19998 	/* Delimit the function signature */
19999 	jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
20000 	if( pEnd >= pGen->pEnd ){
20001 		/* Syntax error */
20002 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
20003 		if( rc == SXERR_ABORT ){
20004 			/* Error count limit reached, abort immediately */
20005 			return SXERR_ABORT;
20006 		}
20007 		pGen->pIn = pGen->pEnd;
20008 		return SXRET_OK;
20009 	}
20010 	/* Create the function state */
20011 	pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
20012 	if( pFunc == 0 ){
20013 		goto OutOfMem;
20014 	}
20015 	/* function ID */
20016 	zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
20017 	if( zName == 0 ){
20018 		/* Don't worry about freeing memory, everything will be released shortly */
20019 		goto OutOfMem;
20020 	}
20021 	/* Initialize the function state */
20022 	jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
20023 	if( pGen->pIn < pEnd ){
20024 		/* Collect function arguments */
20025 		rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
20026 		if( rc == SXERR_ABORT ){
20027 			/* Don't worry about freeing memory, everything will be released shortly */
20028 			return SXERR_ABORT;
20029 		}
20030 	}
20031 	/* Compile function body */
20032 	pGen->pIn = &pEnd[1];
20033 	/* Compile the body */
20034 	rc = GenStateCompileFuncBody(&(*pGen), pFunc);
20035 	if( rc == SXERR_ABORT ){
20036 		return SXERR_ABORT;
20037 	}
20038 	if( ppFunc ){
20039 		*ppFunc = pFunc;
20040 	}
20041 	/* Finally register the function */
20042 	rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
20043 	return rc;
20044 	/* Fall through if something goes wrong */
20045 OutOfMem:
20046 	/* If the supplied memory subsystem is so sick that we are unable to allocate
20047 	 * a tiny chunk of memory, there is no much we can do here.
20048 	 */
20049 	return GenStateOutOfMem(pGen);
20050 }
20051 /*
20052  * Compile a standard JX9 function.
20053  *  Refer to the block-comment above for more information.
20054  */
jx9CompileFunction(jx9_gen_state * pGen)20055 static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
20056 {
20057 	SyString *pName;
20058 	sxi32 iFlags;
20059 	sxu32 nLine;
20060 	sxi32 rc;
20061 
20062 	nLine = pGen->pIn->nLine;
20063 	pGen->pIn++; /* Jump the 'function' keyword */
20064 	iFlags = 0;
20065 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
20066 		/* Invalid function name */
20067 		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
20068 		if( rc == SXERR_ABORT ){
20069 			return SXERR_ABORT;
20070 		}
20071 		/* Sychronize with the next semi-colon or braces*/
20072 		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
20073 			pGen->pIn++;
20074 		}
20075 		return SXRET_OK;
20076 	}
20077 	pName = &pGen->pIn->sData;
20078 	nLine = pGen->pIn->nLine;
20079 	/* Jump the function name */
20080 	pGen->pIn++;
20081 	if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
20082 		/* Syntax error */
20083 		rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
20084 		if( rc == SXERR_ABORT ){
20085 			/* Error count limit reached, abort immediately */
20086 			return SXERR_ABORT;
20087 		}
20088 		/* Sychronize with the next semi-colon or '{' */
20089 		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
20090 			pGen->pIn++;
20091 		}
20092 		return SXRET_OK;
20093 	}
20094 	/* Compile function body */
20095 	rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
20096 	return rc;
20097 }
20098 /*
20099  * Generate bytecode for a given expression tree.
20100  * If something goes wrong while generating bytecode
20101  * for the expression tree (A very unlikely scenario)
20102  * this function takes care of generating the appropriate
20103  * error message.
20104  */
GenStateEmitExprCode(jx9_gen_state * pGen,jx9_expr_node * pNode,sxi32 iFlags)20105 static sxi32 GenStateEmitExprCode(
20106 	jx9_gen_state *pGen,  /* Code generator state */
20107 	jx9_expr_node *pNode, /* Root of the expression tree */
20108 	sxi32 iFlags /* Control flags */
20109 	)
20110 {
20111 	VmInstr *pInstr;
20112 	sxu32 nJmpIdx;
20113 	sxi32 iP1 = 0;
20114 	sxu32 iP2 = 0;
20115 	void *p3  = 0;
20116 	sxi32 iVmOp;
20117 	sxi32 rc;
20118 	if( pNode->xCode ){
20119 		SyToken *pTmpIn, *pTmpEnd;
20120 		/* Compile node */
20121 		SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
20122 		rc = pNode->xCode(&(*pGen), iFlags);
20123 		RE_SWAP_DELIMITER(pGen);
20124 		return rc;
20125 	}
20126 	if( pNode->pOp == 0 ){
20127 		jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine,
20128 			"Invalid expression node, JX9 is aborting compilation");
20129 		return SXERR_ABORT;
20130 	}
20131 	iVmOp = pNode->pOp->iVmOp;
20132 	if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
20133 		sxu32 nJz, nJmp;
20134 		/* Ternary operator require special handling */
20135 		/* Phase#1: Compile the condition */
20136 		rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
20137 		if( rc != SXRET_OK ){
20138 			return rc;
20139 		}
20140 		nJz = nJmp = 0; /* cc -O6 warning */
20141 		/* Phase#2: Emit the false jump */
20142 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
20143 		if( pNode->pLeft ){
20144 			/* Phase#3: Compile the 'then' expression  */
20145 			rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
20146 			if( rc != SXRET_OK ){
20147 				return rc;
20148 			}
20149 		}
20150 		/* Phase#4: Emit the unconditional jump */
20151 		jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
20152 		/* Phase#5: Fix the false jump now the jump destination is resolved. */
20153 		pInstr = jx9VmGetInstr(pGen->pVm, nJz);
20154 		if( pInstr ){
20155 			pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20156 		}
20157 		/* Phase#6: Compile the 'else' expression */
20158 		if( pNode->pRight ){
20159 			rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
20160 			if( rc != SXRET_OK ){
20161 				return rc;
20162 			}
20163 		}
20164 		if( nJmp > 0 ){
20165 			/* Phase#7: Fix the unconditional jump */
20166 			pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
20167 			if( pInstr ){
20168 				pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20169 			}
20170 		}
20171 		/* All done */
20172 		return SXRET_OK;
20173 	}
20174 	/* Generate code for the left tree */
20175 	if( pNode->pLeft ){
20176 		if( iVmOp == JX9_OP_CALL ){
20177 			jx9_expr_node **apNode;
20178 			sxi32 n;
20179 			/* Recurse and generate bytecodes for function arguments */
20180 			apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
20181 			/* Read-only load */
20182 			iFlags |= EXPR_FLAG_RDONLY_LOAD;
20183 			for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
20184 				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
20185 				if( rc != SXRET_OK ){
20186 					return rc;
20187 				}
20188 			}
20189 			/* Total number of given arguments */
20190 			iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
20191 			/* Remove stale flags now */
20192 			iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
20193 		}
20194 		rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
20195 		if( rc != SXRET_OK ){
20196 			return rc;
20197 		}
20198 		if( iVmOp == JX9_OP_CALL ){
20199 			pInstr = jx9VmPeekInstr(pGen->pVm);
20200 			if( pInstr ){
20201 				if ( pInstr->iOp == JX9_OP_LOADC ){
20202 					/* Prevent constant expansion */
20203 					pInstr->iP1 = 0;
20204 				}else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */  ){
20205 					/* Annonymous function call, flag that */
20206 					pInstr->iP2 = 1;
20207 				}
20208 			}
20209 		}else if( iVmOp == JX9_OP_LOAD_IDX ){
20210 			jx9_expr_node **apNode;
20211 			sxi32 n;
20212 			/* Recurse and generate bytecodes for array index */
20213 			apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
20214 			for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
20215 				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
20216 				if( rc != SXRET_OK ){
20217 					return rc;
20218 				}
20219 			}
20220 			if( SySetUsed(&pNode->aNodeArgs) > 0 ){
20221 				iP1 = 1; /* Node have an index associated with it */
20222 			}
20223 			if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
20224 				/* Create an empty entry when the desired index is not found */
20225 				iP2 = 1;
20226 			}
20227 		}else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
20228 			/* POP the left node */
20229 			jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
20230 		}
20231 	}
20232 	rc = SXRET_OK;
20233 	nJmpIdx = 0;
20234 	/* Generate code for the right tree */
20235 	if( pNode->pRight ){
20236 		if( iVmOp == JX9_OP_LAND ){
20237 			/* Emit the false jump so we can short-circuit the logical and */
20238 			jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
20239 		}else if (iVmOp == JX9_OP_LOR ){
20240 			/* Emit the true jump so we can short-circuit the logical or*/
20241 			jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
20242 		}else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
20243 			iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
20244 		}
20245 		rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
20246 		if( iVmOp == JX9_OP_STORE ){
20247 			pInstr = jx9VmPeekInstr(pGen->pVm);
20248 			if( pInstr ){
20249 				if(pInstr->iOp == JX9_OP_MEMBER ){
20250 					/* Perform a member store operation [i.e: $this.x = 50] */
20251 					iP2 = 1;
20252 				}else{
20253 					if( pInstr->iOp == JX9_OP_LOAD_IDX ){
20254 						/* Transform the STORE instruction to STORE_IDX instruction */
20255 						iVmOp = JX9_OP_STORE_IDX;
20256 						iP1 = pInstr->iP1;
20257 					}else{
20258 						p3 = pInstr->p3;
20259 					}
20260 					/* POP the last dynamic load instruction */
20261 					(void)jx9VmPopInstr(pGen->pVm);
20262 				}
20263 			}
20264 		}
20265 	}
20266 	if( iVmOp > 0 ){
20267 		if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
20268 			if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
20269 				/* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
20270 				iP1 = 1;
20271 			}
20272 		}
20273 		/* Finally, emit the VM instruction associated with this operator */
20274 		jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
20275 		if( nJmpIdx > 0 ){
20276 			/* Fix short-circuited jumps now the destination is resolved */
20277 			pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
20278 			if( pInstr ){
20279 				pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
20280 			}
20281 		}
20282 	}
20283 	return rc;
20284 }
20285 /*
20286  * Compile a JX9 expression.
20287  * According to the JX9 language reference manual:
20288  *  Expressions are the most important building stones of JX9.
20289  *  In JX9, almost anything you write is an expression.
20290  *  The simplest yet most accurate way to define an expression
20291  *  is "anything that has a value".
20292  * If something goes wrong while compiling the expression, this
20293  * function takes care of generating the appropriate error
20294  * message.
20295  */
jx9CompileExpr(jx9_gen_state * pGen,sxi32 iFlags,sxi32 (* xTreeValidator)(jx9_gen_state *,jx9_expr_node *))20296 static sxi32 jx9CompileExpr(
20297 	jx9_gen_state *pGen, /* Code generator state */
20298 	sxi32 iFlags,        /* Control flags */
20299 	sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
20300 	)
20301 {
20302 	jx9_expr_node *pRoot;
20303 	SySet sExprNode;
20304 	SyToken *pEnd;
20305 	sxi32 nExpr;
20306 	sxi32 iNest;
20307 	sxi32 rc;
20308 	/* Initialize worker variables */
20309 	nExpr = 0;
20310 	pRoot = 0;
20311 	SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
20312 	SySetAlloc(&sExprNode, 0x10);
20313 	rc = SXRET_OK;
20314 	/* Delimit the expression */
20315 	pEnd = pGen->pIn;
20316 	iNest = 0;
20317 	while( pEnd < pGen->pEnd ){
20318 		if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
20319 			/* Ticket 1433-30: Annonymous/Closure functions body */
20320 			iNest++;
20321 		}else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
20322 			iNest--;
20323 		}else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
20324 			if( iNest <= 0 ){
20325 				break;
20326 			}
20327 		}
20328 		pEnd++;
20329 	}
20330 	if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
20331 		SyToken *pEnd2 = pGen->pIn;
20332 		iNest = 0;
20333 		/* Stop at the first comma */
20334 		while( pEnd2 < pEnd ){
20335 			if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
20336 				iNest++;
20337 			}else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
20338 				iNest--;
20339 			}else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
20340 				if( iNest <= 0 ){
20341 					break;
20342 				}
20343 			}
20344 			pEnd2++;
20345 		}
20346 		if( pEnd2 <pEnd ){
20347 			pEnd = pEnd2;
20348 		}
20349 	}
20350 	if( pEnd > pGen->pIn ){
20351 		SyToken *pTmp = pGen->pEnd;
20352 		/* Swap delimiter */
20353 		pGen->pEnd = pEnd;
20354 		/* Try to get an expression tree */
20355 		rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
20356 		if( rc == SXRET_OK && pRoot ){
20357 			rc = SXRET_OK;
20358 			if( xTreeValidator ){
20359 				/* Call the upper layer validator callback */
20360 				rc = xTreeValidator(&(*pGen), pRoot);
20361 			}
20362 			if( rc != SXERR_ABORT ){
20363 				/* Generate code for the given tree */
20364 				rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
20365 			}
20366 			nExpr = 1;
20367 		}
20368 		/* Release the whole tree */
20369 		jx9ExprFreeTree(&(*pGen), &sExprNode);
20370 		/* Synchronize token stream */
20371 		pGen->pEnd = pTmp;
20372 		pGen->pIn  = pEnd;
20373 		if( rc == SXERR_ABORT ){
20374 			SySetRelease(&sExprNode);
20375 			return SXERR_ABORT;
20376 		}
20377 	}
20378 	SySetRelease(&sExprNode);
20379 	return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
20380 }
20381 /*
20382  * Return a pointer to the node construct handler associated
20383  * with a given node type [i.e: string, integer, float, ...].
20384  */
jx9GetNodeHandler(sxu32 nNodeType)20385 JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
20386 {
20387 	if( nNodeType & JX9_TK_NUM ){
20388 		/* Numeric literal: Either real or integer */
20389 		return jx9CompileNumLiteral;
20390 	}else if( nNodeType & JX9_TK_DSTR ){
20391 		/* Double quoted string */
20392 		return jx9CompileString;
20393 	}else if( nNodeType & JX9_TK_SSTR ){
20394 		/* Single quoted string */
20395 		return jx9CompileSimpleString;
20396 	}else if( nNodeType & JX9_TK_NOWDOC ){
20397 		/* Nowdoc */
20398 		return jx9CompileNowdoc;
20399 	}
20400 	return 0;
20401 }
20402 /*
20403  * Jx9 Language construct table.
20404  */
20405 static const LangConstruct aLangConstruct[] = {
20406 	{ JX9_TKWRD_IF,       jx9CompileIf     },
20407 	{ JX9_TKWRD_FUNCTION, jx9CompileFunction  },
20408 	{ JX9_TKWRD_FOREACH,  jx9CompileForeach },
20409 	{ JX9_TKWRD_WHILE,    jx9CompileWhile  },
20410 	{ JX9_TKWRD_FOR,      jx9CompileFor    },
20411 	{ JX9_TKWRD_SWITCH,   jx9CompileSwitch },
20412 	{ JX9_TKWRD_DIE,      jx9CompileHalt   },
20413 	{ JX9_TKWRD_EXIT,     jx9CompileHalt   },
20414 	{ JX9_TKWRD_RETURN,   jx9CompileReturn },
20415 	{ JX9_TKWRD_BREAK,    jx9CompileBreak  },
20416 	{ JX9_TKWRD_CONTINUE, jx9CompileContinue  },
20417 	{ JX9_TKWRD_STATIC,   jx9CompileStatic    },
20418 	{ JX9_TKWRD_UPLINK,   jx9CompileUplink  },
20419 	{ JX9_TKWRD_CONST,    jx9CompileConstant  },
20420 };
20421 /*
20422  * Return a pointer to the statement handler routine associated
20423  * with a given JX9 keyword [i.e: if, for, while, ...].
20424  */
GenStateGetStatementHandler(sxu32 nKeywordID)20425 static ProcLangConstruct GenStateGetStatementHandler(
20426 	sxu32 nKeywordID   /* Keyword  ID*/
20427 	)
20428 {
20429 	sxu32 n = 0;
20430 	for(;;){
20431 		if( n >= SX_ARRAYSIZE(aLangConstruct) ){
20432 			break;
20433 		}
20434 		if( aLangConstruct[n].nID == nKeywordID ){
20435 			/* Return a pointer to the handler.
20436 			*/
20437 			return aLangConstruct[n].xConstruct;
20438 		}
20439 		n++;
20440 	}
20441 	/* Not a language construct */
20442 	return 0;
20443 }
20444 /*
20445  * Compile a jx9 program.
20446  * If something goes wrong while compiling the Jx9 chunk, this function
20447  * takes care of generating the appropriate error message.
20448  */
GenStateCompileChunk(jx9_gen_state * pGen,sxi32 iFlags)20449 static sxi32 GenStateCompileChunk(
20450 	jx9_gen_state *pGen, /* Code generator state */
20451 	sxi32 iFlags             /* Compile flags */
20452 	)
20453 {
20454 	ProcLangConstruct xCons;
20455 	sxi32 rc;
20456 	rc = SXRET_OK; /* Prevent compiler warning */
20457 	for(;;){
20458 		if( pGen->pIn >= pGen->pEnd ){
20459 			/* No more input to process */
20460 			break;
20461 		}
20462 		xCons = 0;
20463 		if( pGen->pIn->nType & JX9_TK_KEYWORD ){
20464 			sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
20465 			/* Try to extract a language construct handler */
20466 			xCons = GenStateGetStatementHandler(nKeyword);
20467 			if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
20468 				rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
20469 					"Syntax error: Unexpected keyword '%z'",
20470 					&pGen->pIn->sData);
20471 				if( rc == SXERR_ABORT ){
20472 					break;
20473 				}
20474 				/* Synchronize with the first semi-colon and avoid compiling
20475 				 * this erroneous statement.
20476 				 */
20477 				xCons = jx9ErrorRecover;
20478 			}
20479 		}
20480 		if( xCons == 0 ){
20481 			/* Assume an expression an try to compile it */
20482 			rc = jx9CompileExpr(&(*pGen), 0, 0);
20483 			if(  rc != SXERR_EMPTY ){
20484 				/* Pop l-value */
20485 				jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
20486 			}
20487 		}else{
20488 			/* Go compile the sucker */
20489 			rc = xCons(&(*pGen));
20490 		}
20491 		if( rc == SXERR_ABORT ){
20492 			/* Request to abort compilation */
20493 			break;
20494 		}
20495 		/* Ignore trailing semi-colons ';' */
20496 		while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
20497 			pGen->pIn++;
20498 		}
20499 		if( iFlags & JX9_COMPILE_SINGLE_STMT ){
20500 			/* Compile a single statement and return */
20501 			break;
20502 		}
20503 		/* LOOP ONE */
20504 		/* LOOP TWO */
20505 		/* LOOP THREE */
20506 		/* LOOP FOUR */
20507 	}
20508 	/* Return compilation status */
20509 	return rc;
20510 }
20511 /*
20512  * Compile a raw chunk. The raw chunk can contain JX9 code embedded
20513  * in HTML, XML and so on. This function handle all the stuff.
20514  * This is the only compile interface exported from this file.
20515  */
jx9CompileScript(jx9_vm * pVm,SyString * pScript,sxi32 iFlags)20516 JX9_PRIVATE sxi32 jx9CompileScript(
20517 	jx9_vm *pVm,        /* Generate JX9 bytecodes for this Virtual Machine */
20518 	SyString *pScript,  /* Script to compile */
20519 	sxi32 iFlags        /* Compile flags */
20520 	)
20521 {
20522 	jx9_gen_state *pGen;
20523 	SySet aToken;
20524 	sxi32 rc;
20525 	if( pScript->nByte < 1 ){
20526 		/* Nothing to compile */
20527 		return JX9_OK;
20528 	}
20529 	/* Initialize the tokens containers */
20530 	SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
20531 	SySetAlloc(&aToken, 0xc0);
20532 	pGen = &pVm->sCodeGen;
20533 	rc = JX9_OK;
20534 	/* Tokenize the JX9 chunk first */
20535 	jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
20536 	if( SySetUsed(&aToken) < 1 ){
20537 		return SXERR_EMPTY;
20538 	}
20539 	/* Point to the head and tail of the token stream. */
20540 	pGen->pIn  = (SyToken *)SySetBasePtr(&aToken);
20541 	pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
20542 	/* Compile the chunk */
20543 	rc = GenStateCompileChunk(pGen,iFlags);
20544 	/* Cleanup */
20545 	SySetRelease(&aToken);
20546 	return rc;
20547 }
20548 /*
20549  * Utility routines.Initialize the code generator.
20550  */
jx9InitCodeGenerator(jx9_vm * pVm,ProcConsumer xErr,void * pErrData)20551 JX9_PRIVATE sxi32 jx9InitCodeGenerator(
20552 	jx9_vm *pVm,       /* Target VM */
20553 	ProcConsumer xErr, /* Error log consumer callabck  */
20554 	void *pErrData     /* Last argument to xErr() */
20555 	)
20556 {
20557 	jx9_gen_state *pGen = &pVm->sCodeGen;
20558 	/* Zero the structure */
20559 	SyZero(pGen, sizeof(jx9_gen_state));
20560 	/* Initial state */
20561 	pGen->pVm  = &(*pVm);
20562 	pGen->xErr = xErr;
20563 	pGen->pErrData = pErrData;
20564 	SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
20565 	SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
20566 	/* Create the global scope */
20567 	GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
20568 	/* Point to the global scope */
20569 	pGen->pCurrent = &pGen->sGlobal;
20570 	return SXRET_OK;
20571 }
20572 /*
20573  * Utility routines. Reset the code generator to it's initial state.
20574  */
jx9ResetCodeGenerator(jx9_vm * pVm,ProcConsumer xErr,void * pErrData)20575 JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
20576 	jx9_vm *pVm,       /* Target VM */
20577 	ProcConsumer xErr, /* Error log consumer callabck  */
20578 	void *pErrData     /* Last argument to xErr() */
20579 	)
20580 {
20581 	jx9_gen_state *pGen = &pVm->sCodeGen;
20582 	GenBlock *pBlock, *pParent;
20583 	/* Point to the global scope */
20584 	pBlock = pGen->pCurrent;
20585 	while( pBlock->pParent != 0 ){
20586 		pParent = pBlock->pParent;
20587 		GenStateFreeBlock(pBlock);
20588 		pBlock = pParent;
20589 	}
20590 	pGen->xErr = xErr;
20591 	pGen->pErrData = pErrData;
20592 	pGen->pCurrent = &pGen->sGlobal;
20593 	pGen->pIn = pGen->pEnd = 0;
20594 	pGen->nErr = 0;
20595 	return SXRET_OK;
20596 }
20597 /*
20598  * Generate a compile-time error message.
20599  * If the error count limit is reached (usually 15 error message)
20600  * this function return SXERR_ABORT.In that case upper-layers must
20601  * abort compilation immediately.
20602  */
jx9GenCompileError(jx9_gen_state * pGen,sxi32 nErrType,sxu32 nLine,const char * zFormat,...)20603 JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
20604 {
20605 	SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
20606 	const char *zErr = "Error";
20607 	va_list ap;
20608 	if( nErrType == E_ERROR ){
20609 		/* Increment the error counter */
20610 		pGen->nErr++;
20611 		if( pGen->nErr > 15 ){
20612 			/* Error count limit reached */
20613 			SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);
20614 			/* Abort immediately */
20615 			return SXERR_ABORT;
20616 		}
20617 	}
20618 	switch(nErrType){
20619 	case E_WARNING: zErr = "Warning";     break;
20620 	case E_PARSE:   zErr = "Parse error"; break;
20621 	case E_NOTICE:  zErr = "Notice";      break;
20622 	default:
20623 		break;
20624 	}
20625 	/* Format the error message */
20626 	SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
20627 	va_start(ap, zFormat);
20628 	SyBlobFormatAp(pWorker, zFormat, ap);
20629 	va_end(ap);
20630 	/* Append a new line */
20631 	SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
20632 	return JX9_OK;
20633 }
20634 
20635 /* jx9_const.c */
20636 /*
20637  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
20638  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
20639  * Version 1.7.2
20640  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
20641  * please contact Symisc Systems via:
20642  *       legal@symisc.net
20643  *       licensing@symisc.net
20644  *       contact@symisc.net
20645  * or visit:
20646  *      http://jx9.symisc.net/
20647  */
20648  /* $SymiscID: const.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
20649 #ifndef JX9_AMALGAMATION
20650 #include "jx9Int.h"
20651 #endif
20652 /* This file implement built-in constants for the JX9 engine. */
20653 /*
20654  * JX9_VERSION
20655  * __JX9__
20656  *   Expand the current version of the JX9 engine.
20657  */
JX9_VER_Const(jx9_value * pVal,void * pUnused)20658 static void JX9_VER_Const(jx9_value *pVal, void *pUnused)
20659 {
20660 	SXUNUSED(pUnused);
20661 	jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/);
20662 }
20663 #ifdef __WINNT__
20664 #include <Windows.h>
20665 #elif defined(__UNIXES__)
20666 #include <sys/utsname.h>
20667 #endif
20668 /*
20669  * JX9_OS
20670  * __OS__
20671  *  Expand the name of the host Operating System.
20672  */
JX9_OS_Const(jx9_value * pVal,void * pUnused)20673 static void JX9_OS_Const(jx9_value *pVal, void *pUnused)
20674 {
20675 #if defined(__WINNT__)
20676 	jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1);
20677 #elif defined(__UNIXES__)
20678 	struct utsname sInfo;
20679 	if( uname(&sInfo) != 0 ){
20680 		jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1);
20681 	}else{
20682 		jx9_value_string(pVal, sInfo.sysname, -1);
20683 	}
20684 #else
20685 	jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1);
20686 #endif
20687 	SXUNUSED(pUnused);
20688 }
20689 /*
20690  * JX9_EOL
20691  *  Expand the correct 'End Of Line' symbol for this platform.
20692  */
JX9_EOL_Const(jx9_value * pVal,void * pUnused)20693 static void JX9_EOL_Const(jx9_value *pVal, void *pUnused)
20694 {
20695 	SXUNUSED(pUnused);
20696 #ifdef __WINNT__
20697 	jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1);
20698 #else
20699 	jx9_value_string(pVal, "\n", (int)sizeof(char));
20700 #endif
20701 }
20702 /*
20703  * JX9_INT_MAX
20704  * Expand the largest integer supported.
20705  * Note that JX9 deals with 64-bit integer for all platforms.
20706  */
JX9_INTMAX_Const(jx9_value * pVal,void * pUnused)20707 static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused)
20708 {
20709 	SXUNUSED(pUnused);
20710 	jx9_value_int64(pVal, SXI64_HIGH);
20711 }
20712 /*
20713  * JX9_INT_SIZE
20714  * Expand the size in bytes of a 64-bit integer.
20715  */
JX9_INTSIZE_Const(jx9_value * pVal,void * pUnused)20716 static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused)
20717 {
20718 	SXUNUSED(pUnused);
20719 	jx9_value_int64(pVal, sizeof(sxi64));
20720 }
20721 /*
20722  * DIRECTORY_SEPARATOR.
20723  * Expand the directory separator character.
20724  */
JX9_DIRSEP_Const(jx9_value * pVal,void * pUnused)20725 static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused)
20726 {
20727 	SXUNUSED(pUnused);
20728 #ifdef __WINNT__
20729 	jx9_value_string(pVal, "\\", (int)sizeof(char));
20730 #else
20731 	jx9_value_string(pVal, "/", (int)sizeof(char));
20732 #endif
20733 }
20734 /*
20735  * PATH_SEPARATOR.
20736  * Expand the path separator character.
20737  */
JX9_PATHSEP_Const(jx9_value * pVal,void * pUnused)20738 static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused)
20739 {
20740 	SXUNUSED(pUnused);
20741 #ifdef __WINNT__
20742 	jx9_value_string(pVal, ";", (int)sizeof(char));
20743 #else
20744 	jx9_value_string(pVal, ":", (int)sizeof(char));
20745 #endif
20746 }
20747 #ifndef __WINNT__
20748 #include <time.h>
20749 #endif
20750 /*
20751  * __TIME__
20752  *  Expand the current time (GMT).
20753  */
JX9_TIME_Const(jx9_value * pVal,void * pUnused)20754 static void JX9_TIME_Const(jx9_value *pVal, void *pUnused)
20755 {
20756 	Sytm sTm;
20757 #ifdef __WINNT__
20758 	SYSTEMTIME sOS;
20759 	GetSystemTime(&sOS);
20760 	SYSTEMTIME_TO_SYTM(&sOS, &sTm);
20761 #else
20762 	struct tm *pTm;
20763 	time_t t;
20764 	time(&t);
20765 	pTm = gmtime(&t);
20766 	STRUCT_TM_TO_SYTM(pTm, &sTm);
20767 #endif
20768 	SXUNUSED(pUnused); /* cc warning */
20769 	/* Expand */
20770 	jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec);
20771 }
20772 /*
20773  * __DATE__
20774  *  Expand the current date in the ISO-8601 format.
20775  */
JX9_DATE_Const(jx9_value * pVal,void * pUnused)20776 static void JX9_DATE_Const(jx9_value *pVal, void *pUnused)
20777 {
20778 	Sytm sTm;
20779 #ifdef __WINNT__
20780 	SYSTEMTIME sOS;
20781 	GetSystemTime(&sOS);
20782 	SYSTEMTIME_TO_SYTM(&sOS, &sTm);
20783 #else
20784 	struct tm *pTm;
20785 	time_t t;
20786 	time(&t);
20787 	pTm = gmtime(&t);
20788 	STRUCT_TM_TO_SYTM(pTm, &sTm);
20789 #endif
20790 	SXUNUSED(pUnused); /* cc warning */
20791 	/* Expand */
20792 	jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday);
20793 }
20794 /*
20795  * __FILE__
20796  *  Path of the processed script.
20797  */
JX9_FILE_Const(jx9_value * pVal,void * pUserData)20798 static void JX9_FILE_Const(jx9_value *pVal, void *pUserData)
20799 {
20800 	jx9_vm *pVm = (jx9_vm *)pUserData;
20801 	SyString *pFile;
20802 	/* Peek the top entry */
20803 	pFile = (SyString *)SySetPeek(&pVm->aFiles);
20804 	if( pFile == 0 ){
20805 		/* Expand the magic word: ":MEMORY:" */
20806 		jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
20807 	}else{
20808 		jx9_value_string(pVal, pFile->zString, pFile->nByte);
20809 	}
20810 }
20811 /*
20812  * __DIR__
20813  *  Directory holding the processed script.
20814  */
JX9_DIR_Const(jx9_value * pVal,void * pUserData)20815 static void JX9_DIR_Const(jx9_value *pVal, void *pUserData)
20816 {
20817 	jx9_vm *pVm = (jx9_vm *)pUserData;
20818 	SyString *pFile;
20819 	/* Peek the top entry */
20820 	pFile = (SyString *)SySetPeek(&pVm->aFiles);
20821 	if( pFile == 0 ){
20822 		/* Expand the magic word: ":MEMORY:" */
20823 		jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
20824 	}else{
20825 		if( pFile->nByte > 0 ){
20826 			const char *zDir;
20827 			int nLen;
20828 			zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen);
20829 			jx9_value_string(pVal, zDir, nLen);
20830 		}else{
20831 			/* Expand '.' as the current directory*/
20832 			jx9_value_string(pVal, ".", (int)sizeof(char));
20833 		}
20834 	}
20835 }
20836 /*
20837  * E_ERROR
20838  *  Expands 1
20839  */
JX9_E_ERROR_Const(jx9_value * pVal,void * pUserData)20840 static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData)
20841 {
20842 	jx9_value_int(pVal, 1);
20843 	SXUNUSED(pUserData);
20844 }
20845 /*
20846  * E_WARNING
20847  *  Expands 2
20848  */
JX9_E_WARNING_Const(jx9_value * pVal,void * pUserData)20849 static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData)
20850 {
20851 	jx9_value_int(pVal, 2);
20852 	SXUNUSED(pUserData);
20853 }
20854 /*
20855  * E_PARSE
20856  *  Expands 4
20857  */
JX9_E_PARSE_Const(jx9_value * pVal,void * pUserData)20858 static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData)
20859 {
20860 	jx9_value_int(pVal, 4);
20861 	SXUNUSED(pUserData);
20862 }
20863 /*
20864  * E_NOTICE
20865  * Expands 8
20866  */
JX9_E_NOTICE_Const(jx9_value * pVal,void * pUserData)20867 static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData)
20868 {
20869 	jx9_value_int(pVal, 8);
20870 	SXUNUSED(pUserData);
20871 }
20872 /*
20873  * CASE_LOWER
20874  *  Expands 0.
20875  */
JX9_CASE_LOWER_Const(jx9_value * pVal,void * pUserData)20876 static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData)
20877 {
20878 	jx9_value_int(pVal, 0);
20879 	SXUNUSED(pUserData);
20880 }
20881 /*
20882  * CASE_UPPER
20883  *  Expands 1.
20884  */
JX9_CASE_UPPER_Const(jx9_value * pVal,void * pUserData)20885 static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData)
20886 {
20887 	jx9_value_int(pVal, 1);
20888 	SXUNUSED(pUserData);
20889 }
20890 /*
20891  * STR_PAD_LEFT
20892  *  Expands 0.
20893  */
JX9_STR_PAD_LEFT_Const(jx9_value * pVal,void * pUserData)20894 static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData)
20895 {
20896 	jx9_value_int(pVal, 0);
20897 	SXUNUSED(pUserData);
20898 }
20899 /*
20900  * STR_PAD_RIGHT
20901  *  Expands 1.
20902  */
JX9_STR_PAD_RIGHT_Const(jx9_value * pVal,void * pUserData)20903 static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData)
20904 {
20905 	jx9_value_int(pVal, 1);
20906 	SXUNUSED(pUserData);
20907 }
20908 /*
20909  * STR_PAD_BOTH
20910  *  Expands 2.
20911  */
JX9_STR_PAD_BOTH_Const(jx9_value * pVal,void * pUserData)20912 static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData)
20913 {
20914 	jx9_value_int(pVal, 2);
20915 	SXUNUSED(pUserData);
20916 }
20917 /*
20918  * COUNT_NORMAL
20919  *  Expands 0
20920  */
JX9_COUNT_NORMAL_Const(jx9_value * pVal,void * pUserData)20921 static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData)
20922 {
20923 	jx9_value_int(pVal, 0);
20924 	SXUNUSED(pUserData);
20925 }
20926 /*
20927  * COUNT_RECURSIVE
20928  *  Expands 1.
20929  */
JX9_COUNT_RECURSIVE_Const(jx9_value * pVal,void * pUserData)20930 static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData)
20931 {
20932 	jx9_value_int(pVal, 1);
20933 	SXUNUSED(pUserData);
20934 }
20935 /*
20936  * SORT_ASC
20937  *  Expands 1.
20938  */
JX9_SORT_ASC_Const(jx9_value * pVal,void * pUserData)20939 static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData)
20940 {
20941 	jx9_value_int(pVal, 1);
20942 	SXUNUSED(pUserData);
20943 }
20944 /*
20945  * SORT_DESC
20946  *  Expands 2.
20947  */
JX9_SORT_DESC_Const(jx9_value * pVal,void * pUserData)20948 static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData)
20949 {
20950 	jx9_value_int(pVal, 2);
20951 	SXUNUSED(pUserData);
20952 }
20953 /*
20954  * SORT_REGULAR
20955  *  Expands 3.
20956  */
JX9_SORT_REG_Const(jx9_value * pVal,void * pUserData)20957 static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData)
20958 {
20959 	jx9_value_int(pVal, 3);
20960 	SXUNUSED(pUserData);
20961 }
20962 /*
20963  * SORT_NUMERIC
20964  *  Expands 4.
20965  */
JX9_SORT_NUMERIC_Const(jx9_value * pVal,void * pUserData)20966 static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData)
20967 {
20968 	jx9_value_int(pVal, 4);
20969 	SXUNUSED(pUserData);
20970 }
20971 /*
20972  * SORT_STRING
20973  *  Expands 5.
20974  */
JX9_SORT_STRING_Const(jx9_value * pVal,void * pUserData)20975 static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData)
20976 {
20977 	jx9_value_int(pVal, 5);
20978 	SXUNUSED(pUserData);
20979 }
20980 /*
20981  * JX9_ROUND_HALF_UP
20982  *  Expands 1.
20983  */
JX9_JX9_ROUND_HALF_UP_Const(jx9_value * pVal,void * pUserData)20984 static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData)
20985 {
20986 	jx9_value_int(pVal, 1);
20987 	SXUNUSED(pUserData);
20988 }
20989 /*
20990  * SJX9_ROUND_HALF_DOWN
20991  *  Expands 2.
20992  */
JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value * pVal,void * pUserData)20993 static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData)
20994 {
20995 	jx9_value_int(pVal, 2);
20996 	SXUNUSED(pUserData);
20997 }
20998 /*
20999  * JX9_ROUND_HALF_EVEN
21000  *  Expands 3.
21001  */
JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value * pVal,void * pUserData)21002 static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData)
21003 {
21004 	jx9_value_int(pVal, 3);
21005 	SXUNUSED(pUserData);
21006 }
21007 /*
21008  * JX9_ROUND_HALF_ODD
21009  *  Expands 4.
21010  */
JX9_JX9_ROUND_HALF_ODD_Const(jx9_value * pVal,void * pUserData)21011 static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData)
21012 {
21013 	jx9_value_int(pVal, 4);
21014 	SXUNUSED(pUserData);
21015 }
21016 #ifdef JX9_ENABLE_MATH_FUNC
21017 /*
21018  * PI
21019  *  Expand the value of pi.
21020  */
JX9_M_PI_Const(jx9_value * pVal,void * pUserData)21021 static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData)
21022 {
21023 	SXUNUSED(pUserData); /* cc warning */
21024 	jx9_value_double(pVal, JX9_PI);
21025 }
21026 /*
21027  * M_E
21028  *  Expand 2.7182818284590452354
21029  */
JX9_M_E_Const(jx9_value * pVal,void * pUserData)21030 static void JX9_M_E_Const(jx9_value *pVal, void *pUserData)
21031 {
21032 	SXUNUSED(pUserData); /* cc warning */
21033 	jx9_value_double(pVal, 2.7182818284590452354);
21034 }
21035 /*
21036  * M_LOG2E
21037  *  Expand 2.7182818284590452354
21038  */
JX9_M_LOG2E_Const(jx9_value * pVal,void * pUserData)21039 static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData)
21040 {
21041 	SXUNUSED(pUserData); /* cc warning */
21042 	jx9_value_double(pVal, 1.4426950408889634074);
21043 }
21044 /*
21045  * M_LOG10E
21046  *  Expand 0.4342944819032518276
21047  */
JX9_M_LOG10E_Const(jx9_value * pVal,void * pUserData)21048 static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData)
21049 {
21050 	SXUNUSED(pUserData); /* cc warning */
21051 	jx9_value_double(pVal, 0.4342944819032518276);
21052 }
21053 /*
21054  * M_LN2
21055  *  Expand 	0.69314718055994530942
21056  */
JX9_M_LN2_Const(jx9_value * pVal,void * pUserData)21057 static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData)
21058 {
21059 	SXUNUSED(pUserData); /* cc warning */
21060 	jx9_value_double(pVal, 0.69314718055994530942);
21061 }
21062 /*
21063  * M_LN10
21064  *  Expand 	2.30258509299404568402
21065  */
JX9_M_LN10_Const(jx9_value * pVal,void * pUserData)21066 static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData)
21067 {
21068 	SXUNUSED(pUserData); /* cc warning */
21069 	jx9_value_double(pVal, 2.30258509299404568402);
21070 }
21071 /*
21072  * M_PI_2
21073  *  Expand 	1.57079632679489661923
21074  */
JX9_M_PI_2_Const(jx9_value * pVal,void * pUserData)21075 static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData)
21076 {
21077 	SXUNUSED(pUserData); /* cc warning */
21078 	jx9_value_double(pVal, 1.57079632679489661923);
21079 }
21080 /*
21081  * M_PI_4
21082  *  Expand 	0.78539816339744830962
21083  */
JX9_M_PI_4_Const(jx9_value * pVal,void * pUserData)21084 static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData)
21085 {
21086 	SXUNUSED(pUserData); /* cc warning */
21087 	jx9_value_double(pVal, 0.78539816339744830962);
21088 }
21089 /*
21090  * M_1_PI
21091  *  Expand 	0.31830988618379067154
21092  */
JX9_M_1_PI_Const(jx9_value * pVal,void * pUserData)21093 static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData)
21094 {
21095 	SXUNUSED(pUserData); /* cc warning */
21096 	jx9_value_double(pVal, 0.31830988618379067154);
21097 }
21098 /*
21099  * M_2_PI
21100  *  Expand 0.63661977236758134308
21101  */
JX9_M_2_PI_Const(jx9_value * pVal,void * pUserData)21102 static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData)
21103 {
21104 	SXUNUSED(pUserData); /* cc warning */
21105 	jx9_value_double(pVal, 0.63661977236758134308);
21106 }
21107 /*
21108  * M_SQRTPI
21109  *  Expand 1.77245385090551602729
21110  */
JX9_M_SQRTPI_Const(jx9_value * pVal,void * pUserData)21111 static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData)
21112 {
21113 	SXUNUSED(pUserData); /* cc warning */
21114 	jx9_value_double(pVal, 1.77245385090551602729);
21115 }
21116 /*
21117  * M_2_SQRTPI
21118  *  Expand 	1.12837916709551257390
21119  */
JX9_M_2_SQRTPI_Const(jx9_value * pVal,void * pUserData)21120 static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData)
21121 {
21122 	SXUNUSED(pUserData); /* cc warning */
21123 	jx9_value_double(pVal, 1.12837916709551257390);
21124 }
21125 /*
21126  * M_SQRT2
21127  *  Expand 	1.41421356237309504880
21128  */
JX9_M_SQRT2_Const(jx9_value * pVal,void * pUserData)21129 static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData)
21130 {
21131 	SXUNUSED(pUserData); /* cc warning */
21132 	jx9_value_double(pVal, 1.41421356237309504880);
21133 }
21134 /*
21135  * M_SQRT3
21136  *  Expand 	1.73205080756887729352
21137  */
JX9_M_SQRT3_Const(jx9_value * pVal,void * pUserData)21138 static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData)
21139 {
21140 	SXUNUSED(pUserData); /* cc warning */
21141 	jx9_value_double(pVal, 1.73205080756887729352);
21142 }
21143 /*
21144  * M_SQRT1_2
21145  *  Expand 	0.70710678118654752440
21146  */
JX9_M_SQRT1_2_Const(jx9_value * pVal,void * pUserData)21147 static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData)
21148 {
21149 	SXUNUSED(pUserData); /* cc warning */
21150 	jx9_value_double(pVal, 0.70710678118654752440);
21151 }
21152 /*
21153  * M_LNPI
21154  *  Expand 	1.14472988584940017414
21155  */
JX9_M_LNPI_Const(jx9_value * pVal,void * pUserData)21156 static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData)
21157 {
21158 	SXUNUSED(pUserData); /* cc warning */
21159 	jx9_value_double(pVal, 1.14472988584940017414);
21160 }
21161 /*
21162  * M_EULER
21163  *  Expand  0.57721566490153286061
21164  */
JX9_M_EULER_Const(jx9_value * pVal,void * pUserData)21165 static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData)
21166 {
21167 	SXUNUSED(pUserData); /* cc warning */
21168 	jx9_value_double(pVal, 0.57721566490153286061);
21169 }
21170 #endif /* JX9_DISABLE_BUILTIN_MATH */
21171 /*
21172  * DATE_ATOM
21173  *  Expand Atom (example: 2005-08-15T15:52:01+00:00)
21174  */
JX9_DATE_ATOM_Const(jx9_value * pVal,void * pUserData)21175 static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData)
21176 {
21177 	SXUNUSED(pUserData); /* cc warning */
21178 	jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
21179 }
21180 /*
21181  * DATE_COOKIE
21182  *  HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC)
21183  */
JX9_DATE_COOKIE_Const(jx9_value * pVal,void * pUserData)21184 static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData)
21185 {
21186 	SXUNUSED(pUserData); /* cc warning */
21187 	jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
21188 }
21189 /*
21190  * DATE_ISO8601
21191  *  ISO-8601 (example: 2005-08-15T15:52:01+0000)
21192  */
JX9_DATE_ISO8601_Const(jx9_value * pVal,void * pUserData)21193 static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData)
21194 {
21195 	SXUNUSED(pUserData); /* cc warning */
21196 	jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/);
21197 }
21198 /*
21199  * DATE_RFC822
21200  *  RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000)
21201  */
JX9_DATE_RFC822_Const(jx9_value * pVal,void * pUserData)21202 static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData)
21203 {
21204 	SXUNUSED(pUserData); /* cc warning */
21205 	jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
21206 }
21207 /*
21208  * DATE_RFC850
21209  *  RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC)
21210  */
JX9_DATE_RFC850_Const(jx9_value * pVal,void * pUserData)21211 static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData)
21212 {
21213 	SXUNUSED(pUserData); /* cc warning */
21214 	jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
21215 }
21216 /*
21217  * DATE_RFC1036
21218  *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
21219  */
JX9_DATE_RFC1036_Const(jx9_value * pVal,void * pUserData)21220 static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData)
21221 {
21222 	SXUNUSED(pUserData); /* cc warning */
21223 	jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
21224 }
21225 /*
21226  * DATE_RFC1123
21227  *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
21228  */
JX9_DATE_RFC1123_Const(jx9_value * pVal,void * pUserData)21229 static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData)
21230 {
21231 	SXUNUSED(pUserData); /* cc warning */
21232 	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21233 }
21234 /*
21235  * DATE_RFC2822
21236  *  RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000)
21237  */
JX9_DATE_RFC2822_Const(jx9_value * pVal,void * pUserData)21238 static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData)
21239 {
21240 	SXUNUSED(pUserData); /* cc warning */
21241 	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21242 }
21243 /*
21244  * DATE_RSS
21245  *  RSS (Mon, 15 Aug 2005 15:52:01 +0000)
21246  */
JX9_DATE_RSS_Const(jx9_value * pVal,void * pUserData)21247 static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData)
21248 {
21249 	SXUNUSED(pUserData); /* cc warning */
21250 	jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
21251 }
21252 /*
21253  * DATE_W3C
21254  *  World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00)
21255  */
JX9_DATE_W3C_Const(jx9_value * pVal,void * pUserData)21256 static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData)
21257 {
21258 	SXUNUSED(pUserData); /* cc warning */
21259 	jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
21260 }
21261 /*
21262  * ENT_COMPAT
21263  *  Expand 0x01 (Must be a power of two)
21264  */
JX9_ENT_COMPAT_Const(jx9_value * pVal,void * pUserData)21265 static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData)
21266 {
21267 	SXUNUSED(pUserData); /* cc warning */
21268 	jx9_value_int(pVal, 0x01);
21269 }
21270 /*
21271  * ENT_QUOTES
21272  *  Expand 0x02 (Must be a power of two)
21273  */
JX9_ENT_QUOTES_Const(jx9_value * pVal,void * pUserData)21274 static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData)
21275 {
21276 	SXUNUSED(pUserData); /* cc warning */
21277 	jx9_value_int(pVal, 0x02);
21278 }
21279 /*
21280  * ENT_NOQUOTES
21281  *  Expand 0x04 (Must be a power of two)
21282  */
JX9_ENT_NOQUOTES_Const(jx9_value * pVal,void * pUserData)21283 static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData)
21284 {
21285 	SXUNUSED(pUserData); /* cc warning */
21286 	jx9_value_int(pVal, 0x04);
21287 }
21288 /*
21289  * ENT_IGNORE
21290  *  Expand 0x08 (Must be a power of two)
21291  */
JX9_ENT_IGNORE_Const(jx9_value * pVal,void * pUserData)21292 static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData)
21293 {
21294 	SXUNUSED(pUserData); /* cc warning */
21295 	jx9_value_int(pVal, 0x08);
21296 }
21297 /*
21298  * ENT_SUBSTITUTE
21299  *  Expand 0x10 (Must be a power of two)
21300  */
JX9_ENT_SUBSTITUTE_Const(jx9_value * pVal,void * pUserData)21301 static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData)
21302 {
21303 	SXUNUSED(pUserData); /* cc warning */
21304 	jx9_value_int(pVal, 0x10);
21305 }
21306 /*
21307  * ENT_DISALLOWED
21308  *  Expand 0x20 (Must be a power of two)
21309  */
JX9_ENT_DISALLOWED_Const(jx9_value * pVal,void * pUserData)21310 static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData)
21311 {
21312 	SXUNUSED(pUserData); /* cc warning */
21313 	jx9_value_int(pVal, 0x20);
21314 }
21315 /*
21316  * ENT_HTML401
21317  *  Expand 0x40 (Must be a power of two)
21318  */
JX9_ENT_HTML401_Const(jx9_value * pVal,void * pUserData)21319 static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData)
21320 {
21321 	SXUNUSED(pUserData); /* cc warning */
21322 	jx9_value_int(pVal, 0x40);
21323 }
21324 /*
21325  * ENT_XML1
21326  *  Expand 0x80 (Must be a power of two)
21327  */
JX9_ENT_XML1_Const(jx9_value * pVal,void * pUserData)21328 static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData)
21329 {
21330 	SXUNUSED(pUserData); /* cc warning */
21331 	jx9_value_int(pVal, 0x80);
21332 }
21333 /*
21334  * ENT_XHTML
21335  *  Expand 0x100 (Must be a power of two)
21336  */
JX9_ENT_XHTML_Const(jx9_value * pVal,void * pUserData)21337 static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData)
21338 {
21339 	SXUNUSED(pUserData); /* cc warning */
21340 	jx9_value_int(pVal, 0x100);
21341 }
21342 /*
21343  * ENT_HTML5
21344  *  Expand 0x200 (Must be a power of two)
21345  */
JX9_ENT_HTML5_Const(jx9_value * pVal,void * pUserData)21346 static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData)
21347 {
21348 	SXUNUSED(pUserData); /* cc warning */
21349 	jx9_value_int(pVal, 0x200);
21350 }
21351 /*
21352  * ISO-8859-1
21353  * ISO_8859_1
21354  *   Expand 1
21355  */
JX9_ISO88591_Const(jx9_value * pVal,void * pUserData)21356 static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData)
21357 {
21358 	SXUNUSED(pUserData); /* cc warning */
21359 	jx9_value_int(pVal, 1);
21360 }
21361 /*
21362  * UTF-8
21363  * UTF8
21364  *  Expand 2
21365  */
JX9_UTF8_Const(jx9_value * pVal,void * pUserData)21366 static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData)
21367 {
21368 	SXUNUSED(pUserData); /* cc warning */
21369 	jx9_value_int(pVal, 1);
21370 }
21371 /*
21372  * HTML_ENTITIES
21373  *  Expand 1
21374  */
JX9_HTML_ENTITIES_Const(jx9_value * pVal,void * pUserData)21375 static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData)
21376 {
21377 	SXUNUSED(pUserData); /* cc warning */
21378 	jx9_value_int(pVal, 1);
21379 }
21380 /*
21381  * HTML_SPECIALCHARS
21382  *  Expand 2
21383  */
JX9_HTML_SPECIALCHARS_Const(jx9_value * pVal,void * pUserData)21384 static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData)
21385 {
21386 	SXUNUSED(pUserData); /* cc warning */
21387 	jx9_value_int(pVal, 2);
21388 }
21389 /*
21390  * JX9_URL_SCHEME.
21391  * Expand 1
21392  */
JX9_JX9_URL_SCHEME_Const(jx9_value * pVal,void * pUserData)21393 static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData)
21394 {
21395 	SXUNUSED(pUserData); /* cc warning */
21396 	jx9_value_int(pVal, 1);
21397 }
21398 /*
21399  * JX9_URL_HOST.
21400  * Expand 2
21401  */
JX9_JX9_URL_HOST_Const(jx9_value * pVal,void * pUserData)21402 static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData)
21403 {
21404 	SXUNUSED(pUserData); /* cc warning */
21405 	jx9_value_int(pVal, 2);
21406 }
21407 /*
21408  * JX9_URL_PORT.
21409  * Expand 3
21410  */
JX9_JX9_URL_PORT_Const(jx9_value * pVal,void * pUserData)21411 static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData)
21412 {
21413 	SXUNUSED(pUserData); /* cc warning */
21414 	jx9_value_int(pVal, 3);
21415 }
21416 /*
21417  * JX9_URL_USER.
21418  * Expand 4
21419  */
JX9_JX9_URL_USER_Const(jx9_value * pVal,void * pUserData)21420 static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData)
21421 {
21422 	SXUNUSED(pUserData); /* cc warning */
21423 	jx9_value_int(pVal, 4);
21424 }
21425 /*
21426  * JX9_URL_PASS.
21427  * Expand 5
21428  */
JX9_JX9_URL_PASS_Const(jx9_value * pVal,void * pUserData)21429 static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData)
21430 {
21431 	SXUNUSED(pUserData); /* cc warning */
21432 	jx9_value_int(pVal, 5);
21433 }
21434 /*
21435  * JX9_URL_PATH.
21436  * Expand 6
21437  */
JX9_JX9_URL_PATH_Const(jx9_value * pVal,void * pUserData)21438 static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData)
21439 {
21440 	SXUNUSED(pUserData); /* cc warning */
21441 	jx9_value_int(pVal, 6);
21442 }
21443 /*
21444  * JX9_URL_QUERY.
21445  * Expand 7
21446  */
JX9_JX9_URL_QUERY_Const(jx9_value * pVal,void * pUserData)21447 static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData)
21448 {
21449 	SXUNUSED(pUserData); /* cc warning */
21450 	jx9_value_int(pVal, 7);
21451 }
21452 /*
21453  * JX9_URL_FRAGMENT.
21454  * Expand 8
21455  */
JX9_JX9_URL_FRAGMENT_Const(jx9_value * pVal,void * pUserData)21456 static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData)
21457 {
21458 	SXUNUSED(pUserData); /* cc warning */
21459 	jx9_value_int(pVal, 8);
21460 }
21461 /*
21462  * JX9_QUERY_RFC1738
21463  * Expand 1
21464  */
JX9_JX9_QUERY_RFC1738_Const(jx9_value * pVal,void * pUserData)21465 static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData)
21466 {
21467 	SXUNUSED(pUserData); /* cc warning */
21468 	jx9_value_int(pVal, 1);
21469 }
21470 /*
21471  * JX9_QUERY_RFC3986
21472  * Expand 1
21473  */
JX9_JX9_QUERY_RFC3986_Const(jx9_value * pVal,void * pUserData)21474 static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData)
21475 {
21476 	SXUNUSED(pUserData); /* cc warning */
21477 	jx9_value_int(pVal, 2);
21478 }
21479 /*
21480  * FNM_NOESCAPE
21481  *  Expand 0x01 (Must be a power of two)
21482  */
JX9_FNM_NOESCAPE_Const(jx9_value * pVal,void * pUserData)21483 static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
21484 {
21485 	SXUNUSED(pUserData); /* cc warning */
21486 	jx9_value_int(pVal, 0x01);
21487 }
21488 /*
21489  * FNM_PATHNAME
21490  *  Expand 0x02 (Must be a power of two)
21491  */
JX9_FNM_PATHNAME_Const(jx9_value * pVal,void * pUserData)21492 static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData)
21493 {
21494 	SXUNUSED(pUserData); /* cc warning */
21495 	jx9_value_int(pVal, 0x02);
21496 }
21497 /*
21498  * FNM_PERIOD
21499  *  Expand 0x04 (Must be a power of two)
21500  */
JX9_FNM_PERIOD_Const(jx9_value * pVal,void * pUserData)21501 static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData)
21502 {
21503 	SXUNUSED(pUserData); /* cc warning */
21504 	jx9_value_int(pVal, 0x04);
21505 }
21506 /*
21507  * FNM_CASEFOLD
21508  *  Expand 0x08 (Must be a power of two)
21509  */
JX9_FNM_CASEFOLD_Const(jx9_value * pVal,void * pUserData)21510 static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData)
21511 {
21512 	SXUNUSED(pUserData); /* cc warning */
21513 	jx9_value_int(pVal, 0x08);
21514 }
21515 /*
21516  * PATHINFO_DIRNAME
21517  *  Expand 1.
21518  */
JX9_PATHINFO_DIRNAME_Const(jx9_value * pVal,void * pUserData)21519 static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData)
21520 {
21521 	SXUNUSED(pUserData); /* cc warning */
21522 	jx9_value_int(pVal, 1);
21523 }
21524 /*
21525  * PATHINFO_BASENAME
21526  *  Expand 2.
21527  */
JX9_PATHINFO_BASENAME_Const(jx9_value * pVal,void * pUserData)21528 static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData)
21529 {
21530 	SXUNUSED(pUserData); /* cc warning */
21531 	jx9_value_int(pVal, 2);
21532 }
21533 /*
21534  * PATHINFO_EXTENSION
21535  *  Expand 3.
21536  */
JX9_PATHINFO_EXTENSION_Const(jx9_value * pVal,void * pUserData)21537 static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData)
21538 {
21539 	SXUNUSED(pUserData); /* cc warning */
21540 	jx9_value_int(pVal, 3);
21541 }
21542 /*
21543  * PATHINFO_FILENAME
21544  *  Expand 4.
21545  */
JX9_PATHINFO_FILENAME_Const(jx9_value * pVal,void * pUserData)21546 static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData)
21547 {
21548 	SXUNUSED(pUserData); /* cc warning */
21549 	jx9_value_int(pVal, 4);
21550 }
21551 /*
21552  * ASSERT_ACTIVE.
21553  *  Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h
21554  */
JX9_ASSERT_ACTIVE_Const(jx9_value * pVal,void * pUserData)21555 static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData)
21556 {
21557 	SXUNUSED(pUserData); /* cc warning */
21558 	jx9_value_int(pVal, JX9_ASSERT_DISABLE);
21559 }
21560 /*
21561  * ASSERT_WARNING.
21562  *  Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h
21563  */
JX9_ASSERT_WARNING_Const(jx9_value * pVal,void * pUserData)21564 static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData)
21565 {
21566 	SXUNUSED(pUserData); /* cc warning */
21567 	jx9_value_int(pVal, JX9_ASSERT_WARNING);
21568 }
21569 /*
21570  * ASSERT_BAIL.
21571  *  Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h
21572  */
JX9_ASSERT_BAIL_Const(jx9_value * pVal,void * pUserData)21573 static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData)
21574 {
21575 	SXUNUSED(pUserData); /* cc warning */
21576 	jx9_value_int(pVal, JX9_ASSERT_BAIL);
21577 }
21578 /*
21579  * ASSERT_QUIET_EVAL.
21580  *  Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h
21581  */
JX9_ASSERT_QUIET_EVAL_Const(jx9_value * pVal,void * pUserData)21582 static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData)
21583 {
21584 	SXUNUSED(pUserData); /* cc warning */
21585 	jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL);
21586 }
21587 /*
21588  * ASSERT_CALLBACK.
21589  *  Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h
21590  */
JX9_ASSERT_CALLBACK_Const(jx9_value * pVal,void * pUserData)21591 static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData)
21592 {
21593 	SXUNUSED(pUserData); /* cc warning */
21594 	jx9_value_int(pVal, JX9_ASSERT_CALLBACK);
21595 }
21596 /*
21597  * SEEK_SET.
21598  *  Expand 0
21599  */
JX9_SEEK_SET_Const(jx9_value * pVal,void * pUserData)21600 static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData)
21601 {
21602 	SXUNUSED(pUserData); /* cc warning */
21603 	jx9_value_int(pVal, 0);
21604 }
21605 /*
21606  * SEEK_CUR.
21607  *  Expand 1
21608  */
JX9_SEEK_CUR_Const(jx9_value * pVal,void * pUserData)21609 static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData)
21610 {
21611 	SXUNUSED(pUserData); /* cc warning */
21612 	jx9_value_int(pVal, 1);
21613 }
21614 /*
21615  * SEEK_END.
21616  *  Expand 2
21617  */
JX9_SEEK_END_Const(jx9_value * pVal,void * pUserData)21618 static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData)
21619 {
21620 	SXUNUSED(pUserData); /* cc warning */
21621 	jx9_value_int(pVal, 2);
21622 }
21623 /*
21624  * LOCK_SH.
21625  *  Expand 2
21626  */
JX9_LOCK_SH_Const(jx9_value * pVal,void * pUserData)21627 static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData)
21628 {
21629 	SXUNUSED(pUserData); /* cc warning */
21630 	jx9_value_int(pVal, 1);
21631 }
21632 /*
21633  * LOCK_NB.
21634  *  Expand 5
21635  */
JX9_LOCK_NB_Const(jx9_value * pVal,void * pUserData)21636 static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData)
21637 {
21638 	SXUNUSED(pUserData); /* cc warning */
21639 	jx9_value_int(pVal, 5);
21640 }
21641 /*
21642  * LOCK_EX.
21643  *  Expand 0x01 (MUST BE A POWER OF TWO)
21644  */
JX9_LOCK_EX_Const(jx9_value * pVal,void * pUserData)21645 static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData)
21646 {
21647 	SXUNUSED(pUserData); /* cc warning */
21648 	jx9_value_int(pVal, 0x01);
21649 }
21650 /*
21651  * LOCK_UN.
21652  *  Expand 0
21653  */
JX9_LOCK_UN_Const(jx9_value * pVal,void * pUserData)21654 static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData)
21655 {
21656 	SXUNUSED(pUserData); /* cc warning */
21657 	jx9_value_int(pVal, 0);
21658 }
21659 /*
21660  * FILE_USE_INC_PATH
21661  *  Expand 0x01 (Must be a power of two)
21662  */
JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value * pVal,void * pUserData)21663 static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData)
21664 {
21665 	SXUNUSED(pUserData); /* cc warning */
21666 	jx9_value_int(pVal, 0x1);
21667 }
21668 /*
21669  * FILE_IGN_NL
21670  *  Expand 0x02 (Must be a power of two)
21671  */
JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value * pVal,void * pUserData)21672 static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData)
21673 {
21674 	SXUNUSED(pUserData); /* cc warning */
21675 	jx9_value_int(pVal, 0x2);
21676 }
21677 /*
21678  * FILE_SKIP_EL
21679  *  Expand 0x04 (Must be a power of two)
21680  */
JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value * pVal,void * pUserData)21681 static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData)
21682 {
21683 	SXUNUSED(pUserData); /* cc warning */
21684 	jx9_value_int(pVal, 0x4);
21685 }
21686 /*
21687  * FILE_APPEND
21688  *  Expand 0x08 (Must be a power of two)
21689  */
JX9_FILE_APPEND_Const(jx9_value * pVal,void * pUserData)21690 static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData)
21691 {
21692 	SXUNUSED(pUserData); /* cc warning */
21693 	jx9_value_int(pVal, 0x08);
21694 }
21695 /*
21696  * SCANDIR_SORT_ASCENDING
21697  *  Expand 0
21698  */
JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value * pVal,void * pUserData)21699 static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData)
21700 {
21701 	SXUNUSED(pUserData); /* cc warning */
21702 	jx9_value_int(pVal, 0);
21703 }
21704 /*
21705  * SCANDIR_SORT_DESCENDING
21706  *  Expand 1
21707  */
JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value * pVal,void * pUserData)21708 static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData)
21709 {
21710 	SXUNUSED(pUserData); /* cc warning */
21711 	jx9_value_int(pVal, 1);
21712 }
21713 /*
21714  * SCANDIR_SORT_NONE
21715  *  Expand 2
21716  */
JX9_SCANDIR_SORT_NONE_Const(jx9_value * pVal,void * pUserData)21717 static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData)
21718 {
21719 	SXUNUSED(pUserData); /* cc warning */
21720 	jx9_value_int(pVal, 2);
21721 }
21722 /*
21723  * GLOB_MARK
21724  *  Expand 0x01 (must be a power of two)
21725  */
JX9_GLOB_MARK_Const(jx9_value * pVal,void * pUserData)21726 static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData)
21727 {
21728 	SXUNUSED(pUserData); /* cc warning */
21729 	jx9_value_int(pVal, 0x01);
21730 }
21731 /*
21732  * GLOB_NOSORT
21733  *  Expand 0x02 (must be a power of two)
21734  */
JX9_GLOB_NOSORT_Const(jx9_value * pVal,void * pUserData)21735 static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData)
21736 {
21737 	SXUNUSED(pUserData); /* cc warning */
21738 	jx9_value_int(pVal, 0x02);
21739 }
21740 /*
21741  * GLOB_NOCHECK
21742  *  Expand 0x04 (must be a power of two)
21743  */
JX9_GLOB_NOCHECK_Const(jx9_value * pVal,void * pUserData)21744 static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData)
21745 {
21746 	SXUNUSED(pUserData); /* cc warning */
21747 	jx9_value_int(pVal, 0x04);
21748 }
21749 /*
21750  * GLOB_NOESCAPE
21751  *  Expand 0x08 (must be a power of two)
21752  */
JX9_GLOB_NOESCAPE_Const(jx9_value * pVal,void * pUserData)21753 static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
21754 {
21755 	SXUNUSED(pUserData); /* cc warning */
21756 	jx9_value_int(pVal, 0x08);
21757 }
21758 /*
21759  * GLOB_BRACE
21760  *  Expand 0x10 (must be a power of two)
21761  */
JX9_GLOB_BRACE_Const(jx9_value * pVal,void * pUserData)21762 static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData)
21763 {
21764 	SXUNUSED(pUserData); /* cc warning */
21765 	jx9_value_int(pVal, 0x10);
21766 }
21767 /*
21768  * GLOB_ONLYDIR
21769  *  Expand 0x20 (must be a power of two)
21770  */
JX9_GLOB_ONLYDIR_Const(jx9_value * pVal,void * pUserData)21771 static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData)
21772 {
21773 	SXUNUSED(pUserData); /* cc warning */
21774 	jx9_value_int(pVal, 0x20);
21775 }
21776 /*
21777  * GLOB_ERR
21778  *  Expand 0x40 (must be a power of two)
21779  */
JX9_GLOB_ERR_Const(jx9_value * pVal,void * pUserData)21780 static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData)
21781 {
21782 	SXUNUSED(pUserData); /* cc warning */
21783 	jx9_value_int(pVal, 0x40);
21784 }
21785 /*
21786  * STDIN
21787  *  Expand the STDIN handle as a resource.
21788  */
JX9_STDIN_Const(jx9_value * pVal,void * pUserData)21789 static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData)
21790 {
21791 	jx9_vm *pVm = (jx9_vm *)pUserData;
21792 	void *pResource;
21793 	pResource = jx9ExportStdin(pVm);
21794 	jx9_value_resource(pVal, pResource);
21795 }
21796 /*
21797  * STDOUT
21798  *   Expand the STDOUT handle as a resource.
21799  */
JX9_STDOUT_Const(jx9_value * pVal,void * pUserData)21800 static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData)
21801 {
21802 	jx9_vm *pVm = (jx9_vm *)pUserData;
21803 	void *pResource;
21804 	pResource = jx9ExportStdout(pVm);
21805 	jx9_value_resource(pVal, pResource);
21806 }
21807 /*
21808  * STDERR
21809  *  Expand the STDERR handle as a resource.
21810  */
JX9_STDERR_Const(jx9_value * pVal,void * pUserData)21811 static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData)
21812 {
21813 	jx9_vm *pVm = (jx9_vm *)pUserData;
21814 	void *pResource;
21815 	pResource = jx9ExportStderr(pVm);
21816 	jx9_value_resource(pVal, pResource);
21817 }
21818 /*
21819  * INI_SCANNER_NORMAL
21820  *   Expand 1
21821  */
JX9_INI_SCANNER_NORMAL_Const(jx9_value * pVal,void * pUserData)21822 static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData)
21823 {
21824 	SXUNUSED(pUserData); /* cc warning */
21825 	jx9_value_int(pVal, 1);
21826 }
21827 /*
21828  * INI_SCANNER_RAW
21829  *   Expand 2
21830  */
JX9_INI_SCANNER_RAW_Const(jx9_value * pVal,void * pUserData)21831 static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData)
21832 {
21833 	SXUNUSED(pUserData); /* cc warning */
21834 	jx9_value_int(pVal, 2);
21835 }
21836 /*
21837  * EXTR_OVERWRITE
21838  *   Expand 0x01 (Must be a power of two)
21839  */
JX9_EXTR_OVERWRITE_Const(jx9_value * pVal,void * pUserData)21840 static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData)
21841 {
21842 	SXUNUSED(pUserData); /* cc warning */
21843 	jx9_value_int(pVal, 0x1);
21844 }
21845 /*
21846  * EXTR_SKIP
21847  *   Expand 0x02 (Must be a power of two)
21848  */
JX9_EXTR_SKIP_Const(jx9_value * pVal,void * pUserData)21849 static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData)
21850 {
21851 	SXUNUSED(pUserData); /* cc warning */
21852 	jx9_value_int(pVal, 0x2);
21853 }
21854 /*
21855  * EXTR_PREFIX_SAME
21856  *   Expand 0x04 (Must be a power of two)
21857  */
JX9_EXTR_PREFIX_SAME_Const(jx9_value * pVal,void * pUserData)21858 static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData)
21859 {
21860 	SXUNUSED(pUserData); /* cc warning */
21861 	jx9_value_int(pVal, 0x4);
21862 }
21863 /*
21864  * EXTR_PREFIX_ALL
21865  *   Expand 0x08 (Must be a power of two)
21866  */
JX9_EXTR_PREFIX_ALL_Const(jx9_value * pVal,void * pUserData)21867 static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData)
21868 {
21869 	SXUNUSED(pUserData); /* cc warning */
21870 	jx9_value_int(pVal, 0x8);
21871 }
21872 /*
21873  * EXTR_PREFIX_INVALID
21874  *   Expand 0x10 (Must be a power of two)
21875  */
JX9_EXTR_PREFIX_INVALID_Const(jx9_value * pVal,void * pUserData)21876 static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData)
21877 {
21878 	SXUNUSED(pUserData); /* cc warning */
21879 	jx9_value_int(pVal, 0x10);
21880 }
21881 /*
21882  * EXTR_IF_EXISTS
21883  *   Expand 0x20 (Must be a power of two)
21884  */
JX9_EXTR_IF_EXISTS_Const(jx9_value * pVal,void * pUserData)21885 static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
21886 {
21887 	SXUNUSED(pUserData); /* cc warning */
21888 	jx9_value_int(pVal, 0x20);
21889 }
21890 /*
21891  * EXTR_PREFIX_IF_EXISTS
21892  *   Expand 0x40 (Must be a power of two)
21893  */
JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value * pVal,void * pUserData)21894 static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
21895 {
21896 	SXUNUSED(pUserData); /* cc warning */
21897 	jx9_value_int(pVal, 0x40);
21898 }
21899 /*
21900  * Table of built-in constants.
21901  */
21902 static const jx9_builtin_constant aBuiltIn[] = {
21903 	{"JX9_VERSION",          JX9_VER_Const      },
21904 	{"JX9_ENGINE",           JX9_VER_Const      },
21905 	{"__JX9__",              JX9_VER_Const      },
21906 	{"JX9_OS",               JX9_OS_Const       },
21907 	{"__OS__",               JX9_OS_Const       },
21908 	{"JX9_EOL",              JX9_EOL_Const      },
21909 	{"JX9_INT_MAX",          JX9_INTMAX_Const   },
21910 	{"MAXINT",               JX9_INTMAX_Const   },
21911 	{"JX9_INT_SIZE",         JX9_INTSIZE_Const  },
21912 	{"PATH_SEPARATOR",       JX9_PATHSEP_Const  },
21913 	{"DIRECTORY_SEPARATOR",  JX9_DIRSEP_Const   },
21914 	{"DIR_SEP",              JX9_DIRSEP_Const   },
21915 	{"__TIME__",             JX9_TIME_Const     },
21916 	{"__DATE__",             JX9_DATE_Const     },
21917 	{"__FILE__",             JX9_FILE_Const     },
21918 	{"__DIR__",              JX9_DIR_Const      },
21919 	{"E_ERROR",              JX9_E_ERROR_Const  },
21920 	{"E_WARNING",            JX9_E_WARNING_Const},
21921 	{"E_PARSE",              JX9_E_PARSE_Const  },
21922 	{"E_NOTICE",             JX9_E_NOTICE_Const },
21923 	{"CASE_LOWER",           JX9_CASE_LOWER_Const   },
21924 	{"CASE_UPPER",           JX9_CASE_UPPER_Const   },
21925 	{"STR_PAD_LEFT",         JX9_STR_PAD_LEFT_Const },
21926 	{"STR_PAD_RIGHT",        JX9_STR_PAD_RIGHT_Const},
21927 	{"STR_PAD_BOTH",         JX9_STR_PAD_BOTH_Const },
21928 	{"COUNT_NORMAL",         JX9_COUNT_NORMAL_Const },
21929 	{"COUNT_RECURSIVE",      JX9_COUNT_RECURSIVE_Const },
21930 	{"SORT_ASC",             JX9_SORT_ASC_Const     },
21931 	{"SORT_DESC",            JX9_SORT_DESC_Const    },
21932 	{"SORT_REGULAR",         JX9_SORT_REG_Const     },
21933 	{"SORT_NUMERIC",         JX9_SORT_NUMERIC_Const },
21934 	{"SORT_STRING",          JX9_SORT_STRING_Const  },
21935 	{"JX9_ROUND_HALF_DOWN",  JX9_JX9_ROUND_HALF_DOWN_Const },
21936 	{"JX9_ROUND_HALF_EVEN",  JX9_JX9_ROUND_HALF_EVEN_Const },
21937 	{"JX9_ROUND_HALF_UP",    JX9_JX9_ROUND_HALF_UP_Const   },
21938 	{"JX9_ROUND_HALF_ODD",   JX9_JX9_ROUND_HALF_ODD_Const  },
21939 #ifdef JX9_ENABLE_MATH_FUNC
21940 	{"PI",                 JX9_M_PI_Const         },
21941 	{"M_E",                  JX9_M_E_Const          },
21942 	{"M_LOG2E",              JX9_M_LOG2E_Const      },
21943 	{"M_LOG10E",             JX9_M_LOG10E_Const     },
21944 	{"M_LN2",                JX9_M_LN2_Const        },
21945 	{"M_LN10",               JX9_M_LN10_Const       },
21946 	{"M_PI_2",               JX9_M_PI_2_Const       },
21947 	{"M_PI_4",               JX9_M_PI_4_Const       },
21948 	{"M_1_PI",               JX9_M_1_PI_Const       },
21949 	{"M_2_PI",               JX9_M_2_PI_Const       },
21950 	{"M_SQRTPI",             JX9_M_SQRTPI_Const     },
21951 	{"M_2_SQRTPI",           JX9_M_2_SQRTPI_Const   },
21952 	{"M_SQRT2",              JX9_M_SQRT2_Const      },
21953 	{"M_SQRT3",              JX9_M_SQRT3_Const      },
21954 	{"M_SQRT1_2",            JX9_M_SQRT1_2_Const    },
21955 	{"M_LNPI",               JX9_M_LNPI_Const       },
21956 	{"M_EULER",              JX9_M_EULER_Const      },
21957 #endif /* JX9_ENABLE_MATH_FUNC */
21958 	{"DATE_ATOM",            JX9_DATE_ATOM_Const    },
21959 	{"DATE_COOKIE",          JX9_DATE_COOKIE_Const  },
21960 	{"DATE_ISO8601",         JX9_DATE_ISO8601_Const },
21961 	{"DATE_RFC822",          JX9_DATE_RFC822_Const  },
21962 	{"DATE_RFC850",          JX9_DATE_RFC850_Const  },
21963 	{"DATE_RFC1036",         JX9_DATE_RFC1036_Const },
21964 	{"DATE_RFC1123",         JX9_DATE_RFC1123_Const },
21965 	{"DATE_RFC2822",         JX9_DATE_RFC2822_Const },
21966 	{"DATE_RFC3339",         JX9_DATE_ATOM_Const    },
21967 	{"DATE_RSS",             JX9_DATE_RSS_Const     },
21968 	{"DATE_W3C",             JX9_DATE_W3C_Const     },
21969 	{"ENT_COMPAT",           JX9_ENT_COMPAT_Const   },
21970 	{"ENT_QUOTES",           JX9_ENT_QUOTES_Const   },
21971 	{"ENT_NOQUOTES",         JX9_ENT_NOQUOTES_Const },
21972 	{"ENT_IGNORE",           JX9_ENT_IGNORE_Const   },
21973 	{"ENT_SUBSTITUTE",       JX9_ENT_SUBSTITUTE_Const},
21974 	{"ENT_DISALLOWED",       JX9_ENT_DISALLOWED_Const},
21975 	{"ENT_HTML401",          JX9_ENT_HTML401_Const  },
21976 	{"ENT_XML1",             JX9_ENT_XML1_Const     },
21977 	{"ENT_XHTML",            JX9_ENT_XHTML_Const    },
21978 	{"ENT_HTML5",            JX9_ENT_HTML5_Const    },
21979 	{"ISO-8859-1",           JX9_ISO88591_Const     },
21980 	{"ISO_8859_1",           JX9_ISO88591_Const     },
21981 	{"UTF-8",                JX9_UTF8_Const         },
21982 	{"UTF8",                 JX9_UTF8_Const         },
21983 	{"HTML_ENTITIES",        JX9_HTML_ENTITIES_Const},
21984 	{"HTML_SPECIALCHARS",    JX9_HTML_SPECIALCHARS_Const },
21985 	{"JX9_URL_SCHEME",       JX9_JX9_URL_SCHEME_Const},
21986 	{"JX9_URL_HOST",         JX9_JX9_URL_HOST_Const},
21987 	{"JX9_URL_PORT",         JX9_JX9_URL_PORT_Const},
21988 	{"JX9_URL_USER",         JX9_JX9_URL_USER_Const},
21989 	{"JX9_URL_PASS",         JX9_JX9_URL_PASS_Const},
21990 	{"JX9_URL_PATH",         JX9_JX9_URL_PATH_Const},
21991 	{"JX9_URL_QUERY",        JX9_JX9_URL_QUERY_Const},
21992 	{"JX9_URL_FRAGMENT",     JX9_JX9_URL_FRAGMENT_Const},
21993 	{"JX9_QUERY_RFC1738",    JX9_JX9_QUERY_RFC1738_Const},
21994 	{"JX9_QUERY_RFC3986",    JX9_JX9_QUERY_RFC3986_Const},
21995 	{"FNM_NOESCAPE",         JX9_FNM_NOESCAPE_Const },
21996 	{"FNM_PATHNAME",         JX9_FNM_PATHNAME_Const },
21997 	{"FNM_PERIOD",           JX9_FNM_PERIOD_Const   },
21998 	{"FNM_CASEFOLD",         JX9_FNM_CASEFOLD_Const },
21999 	{"PATHINFO_DIRNAME",     JX9_PATHINFO_DIRNAME_Const  },
22000 	{"PATHINFO_BASENAME",    JX9_PATHINFO_BASENAME_Const },
22001 	{"PATHINFO_EXTENSION",   JX9_PATHINFO_EXTENSION_Const},
22002 	{"PATHINFO_FILENAME",    JX9_PATHINFO_FILENAME_Const },
22003 	{"ASSERT_ACTIVE",        JX9_ASSERT_ACTIVE_Const     },
22004 	{"ASSERT_WARNING",       JX9_ASSERT_WARNING_Const    },
22005 	{"ASSERT_BAIL",          JX9_ASSERT_BAIL_Const       },
22006 	{"ASSERT_QUIET_EVAL",    JX9_ASSERT_QUIET_EVAL_Const },
22007 	{"ASSERT_CALLBACK",      JX9_ASSERT_CALLBACK_Const   },
22008 	{"SEEK_SET",             JX9_SEEK_SET_Const      },
22009 	{"SEEK_CUR",             JX9_SEEK_CUR_Const      },
22010 	{"SEEK_END",             JX9_SEEK_END_Const      },
22011 	{"LOCK_EX",              JX9_LOCK_EX_Const      },
22012 	{"LOCK_SH",              JX9_LOCK_SH_Const      },
22013 	{"LOCK_NB",              JX9_LOCK_NB_Const      },
22014 	{"LOCK_UN",              JX9_LOCK_UN_Const      },
22015 	{"FILE_USE_INC_PATH",    JX9_FILE_USE_INCLUDE_PATH_Const},
22016 	{"FILE_IGN_NL",          JX9_FILE_IGNORE_NEW_LINES_Const},
22017 	{"FILE_SKIP_EL",         JX9_FILE_SKIP_EMPTY_LINES_Const},
22018 	{"FILE_APPEND",          JX9_FILE_APPEND_Const },
22019 	{"SCANDIR_SORT_ASC",     JX9_SCANDIR_SORT_ASCENDING_Const  },
22020 	{"SCANDIR_SORT_DESC",    JX9_SCANDIR_SORT_DESCENDING_Const },
22021 	{"SCANDIR_SORT_NONE",    JX9_SCANDIR_SORT_NONE_Const },
22022 	{"GLOB_MARK",            JX9_GLOB_MARK_Const    },
22023 	{"GLOB_NOSORT",          JX9_GLOB_NOSORT_Const  },
22024 	{"GLOB_NOCHECK",         JX9_GLOB_NOCHECK_Const },
22025 	{"GLOB_NOESCAPE",        JX9_GLOB_NOESCAPE_Const},
22026 	{"GLOB_BRACE",           JX9_GLOB_BRACE_Const   },
22027 	{"GLOB_ONLYDIR",         JX9_GLOB_ONLYDIR_Const },
22028 	{"GLOB_ERR",             JX9_GLOB_ERR_Const     },
22029 	{"STDIN",                JX9_STDIN_Const        },
22030 	{"stdin",                JX9_STDIN_Const        },
22031 	{"STDOUT",               JX9_STDOUT_Const       },
22032 	{"stdout",               JX9_STDOUT_Const       },
22033 	{"STDERR",               JX9_STDERR_Const       },
22034 	{"stderr",               JX9_STDERR_Const       },
22035 	{"INI_SCANNER_NORMAL",   JX9_INI_SCANNER_NORMAL_Const },
22036 	{"INI_SCANNER_RAW",      JX9_INI_SCANNER_RAW_Const    },
22037 	{"EXTR_OVERWRITE",       JX9_EXTR_OVERWRITE_Const     },
22038 	{"EXTR_SKIP",            JX9_EXTR_SKIP_Const        },
22039 	{"EXTR_PREFIX_SAME",     JX9_EXTR_PREFIX_SAME_Const },
22040 	{"EXTR_PREFIX_ALL",      JX9_EXTR_PREFIX_ALL_Const  },
22041 	{"EXTR_PREFIX_INVALID",  JX9_EXTR_PREFIX_INVALID_Const },
22042 	{"EXTR_IF_EXISTS",       JX9_EXTR_IF_EXISTS_Const   },
22043 	{"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const}
22044 };
22045 /*
22046  * Register the built-in constants defined above.
22047  */
jx9RegisterBuiltInConstant(jx9_vm * pVm)22048 JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm)
22049 {
22050 	sxu32 n;
22051 	/*
22052 	 * Note that all built-in constants have access to the jx9 virtual machine
22053 	 * that trigger the constant invocation as their private data.
22054 	 */
22055 	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){
22056 		jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm));
22057 	}
22058 }
22059 
22060 /* jx9_hashmap.c */
22061 /*
22062  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
22063  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
22064  * Version 1.7.2
22065  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
22066  * please contact Symisc Systems via:
22067  *       legal@symisc.net
22068  *       licensing@symisc.net
22069  *       contact@symisc.net
22070  * or visit:
22071  *      http://jx9.symisc.net/
22072  */
22073  /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable <chm@symisc.net> $ */
22074 #ifndef JX9_AMALGAMATION
22075 #include "jx9Int.h"
22076 #endif
22077 /* This file implement generic hashmaps used to represent JSON arrays and objects */
22078 /* Allowed node types */
22079 #define HASHMAP_INT_NODE   1  /* Node with an int [i.e: 64-bit integer] key */
22080 #define HASHMAP_BLOB_NODE  2  /* Node with a string/BLOB key */
22081 /*
22082  * Default hash function for int [i.e; 64-bit integer] keys.
22083  */
IntHash(sxi64 iKey)22084 static sxu32 IntHash(sxi64 iKey)
22085 {
22086 	return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
22087 }
22088 /*
22089  * Default hash function for string/BLOB keys.
22090  */
BinHash(const void * pSrc,sxu32 nLen)22091 static sxu32 BinHash(const void *pSrc, sxu32 nLen)
22092 {
22093 	register unsigned char *zIn = (unsigned char *)pSrc;
22094 	unsigned char *zEnd;
22095 	sxu32 nH = 5381;
22096 	zEnd = &zIn[nLen];
22097 	for(;;){
22098 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22099 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22100 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22101 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
22102 	}
22103 	return nH;
22104 }
22105 /*
22106  * Return the total number of entries in a given hashmap.
22107  * If bRecurisve is set to TRUE then recurse on hashmap entries.
22108  * If the nesting limit is reached, this function abort immediately.
22109  */
HashmapCount(jx9_hashmap * pMap,int bRecursive,int iRecCount)22110 static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount)
22111 {
22112 	sxi64 iCount = 0;
22113 	if( !bRecursive ){
22114 		iCount = pMap->nEntry;
22115 	}else{
22116 		/* Recursive hashmap walk */
22117 		jx9_hashmap_node *pEntry = pMap->pLast;
22118 		jx9_value *pElem;
22119 		sxu32 n = 0;
22120 		for(;;){
22121 			if( n >= pMap->nEntry ){
22122 				break;
22123 			}
22124 			/* Point to the element value */
22125 			pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
22126 			if( pElem ){
22127 				if( pElem->iFlags & MEMOBJ_HASHMAP ){
22128 					if( iRecCount > 31 ){
22129 						/* Nesting limit reached */
22130 						return iCount;
22131 					}
22132 					/* Recurse */
22133 					iRecCount++;
22134 					iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount);
22135 					iRecCount--;
22136 				}
22137 			}
22138 			/* Point to the next entry */
22139 			pEntry = pEntry->pNext;
22140 			++n;
22141 		}
22142 		/* Update count */
22143 		iCount += pMap->nEntry;
22144 	}
22145 	return iCount;
22146 }
22147 /*
22148  * Allocate a new hashmap node with a 64-bit integer key.
22149  * If something goes wrong [i.e: out of memory], this function return NULL.
22150  * Otherwise a fresh [jx9_hashmap_node] instance is returned.
22151  */
HashmapNewIntNode(jx9_hashmap * pMap,sxi64 iKey,sxu32 nHash,sxu32 nValIdx)22152 static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx)
22153 {
22154 	jx9_hashmap_node *pNode;
22155 	/* Allocate a new node */
22156 	pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
22157 	if( pNode == 0 ){
22158 		return 0;
22159 	}
22160 	/* Zero the stucture */
22161 	SyZero(pNode, sizeof(jx9_hashmap_node));
22162 	/* Fill in the structure */
22163 	pNode->pMap  = &(*pMap);
22164 	pNode->iType = HASHMAP_INT_NODE;
22165 	pNode->nHash = nHash;
22166 	pNode->xKey.iKey = iKey;
22167 	pNode->nValIdx  = nValIdx;
22168 	return pNode;
22169 }
22170 /*
22171  * Allocate a new hashmap node with a BLOB key.
22172  * If something goes wrong [i.e: out of memory], this function return NULL.
22173  * Otherwise a fresh [jx9_hashmap_node] instance is returned.
22174  */
HashmapNewBlobNode(jx9_hashmap * pMap,const void * pKey,sxu32 nKeyLen,sxu32 nHash,sxu32 nValIdx)22175 static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx)
22176 {
22177 	jx9_hashmap_node *pNode;
22178 	/* Allocate a new node */
22179 	pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
22180 	if( pNode == 0 ){
22181 		return 0;
22182 	}
22183 	/* Zero the stucture */
22184 	SyZero(pNode, sizeof(jx9_hashmap_node));
22185 	/* Fill in the structure */
22186 	pNode->pMap  = &(*pMap);
22187 	pNode->iType = HASHMAP_BLOB_NODE;
22188 	pNode->nHash = nHash;
22189 	SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
22190 	SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
22191 	pNode->nValIdx = nValIdx;
22192 	return pNode;
22193 }
22194 /*
22195  * link a hashmap node to the given bucket index (last argument to this function).
22196  */
HashmapNodeLink(jx9_hashmap * pMap,jx9_hashmap_node * pNode,sxu32 nBucketIdx)22197 static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx)
22198 {
22199 	/* Link */
22200 	if( pMap->apBucket[nBucketIdx] != 0 ){
22201 		pNode->pNextCollide = pMap->apBucket[nBucketIdx];
22202 		pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
22203 	}
22204 	pMap->apBucket[nBucketIdx] = pNode;
22205 	/* Link to the map list */
22206 	if( pMap->pFirst == 0 ){
22207 		pMap->pFirst = pMap->pLast = pNode;
22208 		/* Point to the first inserted node */
22209 		pMap->pCur = pNode;
22210 	}else{
22211 		MACRO_LD_PUSH(pMap->pLast, pNode);
22212 	}
22213 	++pMap->nEntry;
22214 }
22215 /*
22216  * Unlink a node from the hashmap.
22217  * If the node count reaches zero then release the whole hash-bucket.
22218  */
jx9HashmapUnlinkNode(jx9_hashmap_node * pNode)22219 static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode)
22220 {
22221 	jx9_hashmap *pMap = pNode->pMap;
22222 	jx9_vm *pVm = pMap->pVm;
22223 	/* Unlink from the corresponding bucket */
22224 	if( pNode->pPrevCollide == 0 ){
22225 		pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
22226 	}else{
22227 		pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
22228 	}
22229 	if( pNode->pNextCollide ){
22230 		pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
22231 	}
22232 	if( pMap->pFirst == pNode ){
22233 		pMap->pFirst = pNode->pPrev;
22234 	}
22235 	if( pMap->pCur == pNode ){
22236 		/* Advance the node cursor */
22237 		pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
22238 	}
22239 	/* Unlink from the map list */
22240 	MACRO_LD_REMOVE(pMap->pLast, pNode);
22241 	/* Restore to the free list */
22242 	jx9VmUnsetMemObj(pVm, pNode->nValIdx);
22243 	if( pNode->iType == HASHMAP_BLOB_NODE ){
22244 		SyBlobRelease(&pNode->xKey.sKey);
22245 	}
22246 	SyMemBackendPoolFree(&pVm->sAllocator, pNode);
22247 	pMap->nEntry--;
22248 	if( pMap->nEntry < 1 ){
22249 		/* Free the hash-bucket */
22250 		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
22251 		pMap->apBucket = 0;
22252 		pMap->nSize = 0;
22253 		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
22254 	}
22255 }
22256 #define HASHMAP_FILL_FACTOR 3
22257 /*
22258  * Grow the hash-table and rehash all entries.
22259  */
HashmapGrowBucket(jx9_hashmap * pMap)22260 static sxi32 HashmapGrowBucket(jx9_hashmap *pMap)
22261 {
22262 	if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){
22263 		jx9_hashmap_node **apOld = pMap->apBucket;
22264 		jx9_hashmap_node *pEntry, **apNew;
22265 		sxu32 nNew = pMap->nSize << 1;
22266 		sxu32 nBucket;
22267 		sxu32 n;
22268 		if( nNew < 1 ){
22269 			nNew = 16;
22270 		}
22271 		/* Allocate a new bucket */
22272 		apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *));
22273 		if( apNew == 0 ){
22274 			if( pMap->nSize < 1 ){
22275 				return SXERR_MEM; /* Fatal */
22276 			}
22277 			/* Not so fatal here, simply a performance hit */
22278 			return SXRET_OK;
22279 		}
22280 		/* Zero the table */
22281 		SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *));
22282 		/* Reflect the change */
22283 		pMap->apBucket = apNew;
22284 		pMap->nSize = nNew;
22285 		if( apOld == 0 ){
22286 			/* First allocated table [i.e: no entry], return immediately */
22287 			return SXRET_OK;
22288 		}
22289 		/* Rehash old entries */
22290 		pEntry = pMap->pFirst;
22291 		n = 0;
22292 		for( ;; ){
22293 			if( n >= pMap->nEntry ){
22294 				break;
22295 			}
22296 			/* Clear the old collision link */
22297 			pEntry->pNextCollide = pEntry->pPrevCollide = 0;
22298 			/* Link to the new bucket */
22299 			nBucket = pEntry->nHash & (nNew - 1);
22300 			if( pMap->apBucket[nBucket] != 0 ){
22301 				pEntry->pNextCollide = pMap->apBucket[nBucket];
22302 				pMap->apBucket[nBucket]->pPrevCollide = pEntry;
22303 			}
22304 			pMap->apBucket[nBucket] = pEntry;
22305 			/* Point to the next entry */
22306 			pEntry = pEntry->pPrev; /* Reverse link */
22307 			n++;
22308 		}
22309 		/* Free the old table */
22310 		SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
22311 	}
22312 	return SXRET_OK;
22313 }
22314 /*
22315  * Insert a 64-bit integer key and it's associated value (if any) in the given
22316  * hashmap.
22317  */
HashmapInsertIntKey(jx9_hashmap * pMap,sxi64 iKey,jx9_value * pValue)22318 static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue)
22319 {
22320 	jx9_hashmap_node *pNode;
22321 	jx9_value *pObj;
22322 	sxu32 nIdx;
22323 	sxu32 nHash;
22324 	sxi32 rc;
22325 	/* Reserve a jx9_value for the value */
22326 	pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
22327 	if( pObj == 0 ){
22328 		return SXERR_MEM;
22329 	}
22330 	if( pValue ){
22331 		/* Duplicate the value */
22332 		jx9MemObjStore(pValue, pObj);
22333 	}
22334 	/* Hash the key */
22335 	nHash = pMap->xIntHash(iKey);
22336 	/* Allocate a new int node */
22337 	pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
22338 	if( pNode == 0 ){
22339 		return SXERR_MEM;
22340 	}
22341 	/* Make sure the bucket is big enough to hold the new entry */
22342 	rc = HashmapGrowBucket(&(*pMap));
22343 	if( rc != SXRET_OK ){
22344 		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
22345 		return rc;
22346 	}
22347 	/* Perform the insertion */
22348 	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
22349 	/* All done */
22350 	return SXRET_OK;
22351 }
22352 /*
22353  * Insert a BLOB key and it's associated value (if any) in the given
22354  * hashmap.
22355  */
HashmapInsertBlobKey(jx9_hashmap * pMap,const void * pKey,sxu32 nKeyLen,jx9_value * pValue)22356 static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue)
22357 {
22358 	jx9_hashmap_node *pNode;
22359 	jx9_value *pObj;
22360 	sxu32 nHash;
22361 	sxu32 nIdx;
22362 	sxi32 rc;
22363 	/* Reserve a jx9_value for the value */
22364 	pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
22365 	if( pObj == 0 ){
22366 		return SXERR_MEM;
22367 	}
22368 	if( pValue ){
22369 		/* Duplicate the value */
22370 		jx9MemObjStore(pValue, pObj);
22371 	}
22372 	/* Hash the key */
22373 	nHash = pMap->xBlobHash(pKey, nKeyLen);
22374 	/* Allocate a new blob node */
22375 	pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
22376 	if( pNode == 0 ){
22377 		return SXERR_MEM;
22378 	}
22379 	/* Make sure the bucket is big enough to hold the new entry */
22380 	rc = HashmapGrowBucket(&(*pMap));
22381 	if( rc != SXRET_OK ){
22382 		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
22383 		return rc;
22384 	}
22385 	/* Perform the insertion */
22386 	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
22387 	/* All done */
22388 	return SXRET_OK;
22389 }
22390 /*
22391  * Check if a given 64-bit integer key exists in the given hashmap.
22392  * Write a pointer to the target node on success. Otherwise
22393  * SXERR_NOTFOUND is returned on failure.
22394  */
HashmapLookupIntKey(jx9_hashmap * pMap,sxi64 iKey,jx9_hashmap_node ** ppNode)22395 static sxi32 HashmapLookupIntKey(
22396 	jx9_hashmap *pMap,         /* Target hashmap */
22397 	sxi64 iKey,                /* lookup key */
22398 	jx9_hashmap_node **ppNode  /* OUT: target node on success */
22399 	)
22400 {
22401 	jx9_hashmap_node *pNode;
22402 	sxu32 nHash;
22403 	if( pMap->nEntry < 1 ){
22404 		/* Don't bother hashing, there is no entry anyway */
22405 		return SXERR_NOTFOUND;
22406 	}
22407 	/* Hash the key first */
22408 	nHash = pMap->xIntHash(iKey);
22409 	/* Point to the appropriate bucket */
22410 	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
22411 	/* Perform the lookup */
22412 	for(;;){
22413 		if( pNode == 0 ){
22414 			break;
22415 		}
22416 		if( pNode->iType == HASHMAP_INT_NODE
22417 			&& pNode->nHash == nHash
22418 			&& pNode->xKey.iKey == iKey ){
22419 				/* Node found */
22420 				if( ppNode ){
22421 					*ppNode = pNode;
22422 				}
22423 				return SXRET_OK;
22424 		}
22425 		/* Follow the collision link */
22426 		pNode = pNode->pNextCollide;
22427 	}
22428 	/* No such entry */
22429 	return SXERR_NOTFOUND;
22430 }
22431 /*
22432  * Check if a given BLOB key exists in the given hashmap.
22433  * Write a pointer to the target node on success. Otherwise
22434  * SXERR_NOTFOUND is returned on failure.
22435  */
HashmapLookupBlobKey(jx9_hashmap * pMap,const void * pKey,sxu32 nKeyLen,jx9_hashmap_node ** ppNode)22436 static sxi32 HashmapLookupBlobKey(
22437 	jx9_hashmap *pMap,          /* Target hashmap */
22438 	const void *pKey,           /* Lookup key */
22439 	sxu32 nKeyLen,              /* Key length in bytes */
22440 	jx9_hashmap_node **ppNode   /* OUT: target node on success */
22441 	)
22442 {
22443 	jx9_hashmap_node *pNode;
22444 	sxu32 nHash;
22445 	if( pMap->nEntry < 1 ){
22446 		/* Don't bother hashing, there is no entry anyway */
22447 		return SXERR_NOTFOUND;
22448 	}
22449 	/* Hash the key first */
22450 	nHash = pMap->xBlobHash(pKey, nKeyLen);
22451 	/* Point to the appropriate bucket */
22452 	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
22453 	/* Perform the lookup */
22454 	for(;;){
22455 		if( pNode == 0 ){
22456 			break;
22457 		}
22458 		if( pNode->iType == HASHMAP_BLOB_NODE
22459 			&& pNode->nHash == nHash
22460 			&& SyBlobLength(&pNode->xKey.sKey) == nKeyLen
22461 			&& SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){
22462 				/* Node found */
22463 				if( ppNode ){
22464 					*ppNode = pNode;
22465 				}
22466 				return SXRET_OK;
22467 		}
22468 		/* Follow the collision link */
22469 		pNode = pNode->pNextCollide;
22470 	}
22471 	/* No such entry */
22472 	return SXERR_NOTFOUND;
22473 }
22474 /*
22475  * Check if the given BLOB key looks like a decimal number.
22476  * Retrurn TRUE on success.FALSE otherwise.
22477  */
HashmapIsIntKey(SyBlob * pKey)22478 static int HashmapIsIntKey(SyBlob *pKey)
22479 {
22480 	const char *zIn  = (const char *)SyBlobData(pKey);
22481 	const char *zEnd = &zIn[SyBlobLength(pKey)];
22482 	if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){
22483 		/* Octal not decimal number */
22484 		return FALSE;
22485 	}
22486 	if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){
22487 		zIn++;
22488 	}
22489 	for(;;){
22490 		if( zIn >= zEnd ){
22491 			return TRUE;
22492 		}
22493 		if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */  || !SyisDigit(zIn[0]) ){
22494 			break;
22495 		}
22496 		zIn++;
22497 	}
22498 	/* Key does not look like a decimal number */
22499 	return FALSE;
22500 }
22501 /*
22502  * Check if a given key exists in the given hashmap.
22503  * Write a pointer to the target node on success.
22504  * Otherwise SXERR_NOTFOUND is returned on failure.
22505  */
HashmapLookup(jx9_hashmap * pMap,jx9_value * pKey,jx9_hashmap_node ** ppNode)22506 static sxi32 HashmapLookup(
22507 	jx9_hashmap *pMap,          /* Target hashmap */
22508 	jx9_value *pKey,            /* Lookup key */
22509 	jx9_hashmap_node **ppNode   /* OUT: target node on success */
22510 	)
22511 {
22512 	jx9_hashmap_node *pNode = 0; /* cc -O6 warning */
22513 	sxi32 rc;
22514 	if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
22515 		if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
22516 			/* Force a string cast */
22517 			jx9MemObjToString(&(*pKey));
22518 		}
22519 		if( SyBlobLength(&pKey->sBlob) > 0 ){
22520 			/* Perform a blob lookup */
22521 			rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
22522 			goto result;
22523 		}
22524 	}
22525 	/* Perform an int lookup */
22526 	if((pKey->iFlags & MEMOBJ_INT) == 0 ){
22527 		/* Force an integer cast */
22528 		jx9MemObjToInteger(pKey);
22529 	}
22530 	/* Perform an int lookup */
22531 	rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
22532 result:
22533 	if( rc == SXRET_OK ){
22534 		/* Node found */
22535 		if( ppNode ){
22536 			*ppNode = pNode;
22537 		}
22538 		return SXRET_OK;
22539 	}
22540 	/* No such entry */
22541 	return SXERR_NOTFOUND;
22542 }
22543 /*
22544  * Insert a given key and it's associated value (if any) in the given
22545  * hashmap.
22546  * If a node with the given key already exists in the database
22547  * then this function overwrite the old value.
22548  */
HashmapInsert(jx9_hashmap * pMap,jx9_value * pKey,jx9_value * pVal)22549 static sxi32 HashmapInsert(
22550 	jx9_hashmap *pMap, /* Target hashmap */
22551 	jx9_value *pKey,   /* Lookup key  */
22552 	jx9_value *pVal    /* Node value */
22553 	)
22554 {
22555 	jx9_hashmap_node *pNode = 0;
22556 	sxi32 rc = SXRET_OK;
22557 	if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){
22558 		pMap->iFlags |= HASHMAP_JSON_OBJECT;
22559 	}
22560 	if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){
22561 		if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
22562 			/* Force a string cast */
22563 			jx9MemObjToString(&(*pKey));
22564 		}
22565 		if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){
22566 			if(SyBlobLength(&pKey->sBlob) < 1){
22567 				/* Automatic index assign */
22568 				pKey = 0;
22569 			}
22570 			goto IntKey;
22571 		}
22572 		if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
22573 			SyBlobLength(&pKey->sBlob), &pNode) ){
22574 				/* Overwrite the old value */
22575 				jx9_value *pElem;
22576 				pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
22577 				if( pElem ){
22578 					if( pVal ){
22579 						jx9MemObjStore(pVal, pElem);
22580 					}else{
22581 						/* Nullify the entry */
22582 						jx9MemObjToNull(pElem);
22583 					}
22584 				}
22585 				return SXRET_OK;
22586 		}
22587 		/* Perform a blob-key insertion */
22588 		rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal));
22589 		return rc;
22590 	}
22591 IntKey:
22592 	if( pKey ){
22593 		if((pKey->iFlags & MEMOBJ_INT) == 0 ){
22594 			/* Force an integer cast */
22595 			jx9MemObjToInteger(pKey);
22596 		}
22597 		if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){
22598 			/* Overwrite the old value */
22599 			jx9_value *pElem;
22600 			pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
22601 			if( pElem ){
22602 				if( pVal ){
22603 					jx9MemObjStore(pVal, pElem);
22604 				}else{
22605 					/* Nullify the entry */
22606 					jx9MemObjToNull(pElem);
22607 				}
22608 			}
22609 			return SXRET_OK;
22610 		}
22611 		/* Perform a 64-bit-int-key insertion */
22612 		rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal));
22613 		if( rc == SXRET_OK ){
22614 			if( pKey->x.iVal >= pMap->iNextIdx ){
22615 				/* Increment the automatic index */
22616 				pMap->iNextIdx = pKey->x.iVal + 1;
22617 				/* Make sure the automatic index is not reserved */
22618 				while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){
22619 					pMap->iNextIdx++;
22620 				}
22621 			}
22622 		}
22623 	}else{
22624 		/* Assign an automatic index */
22625 		rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal));
22626 		if( rc == SXRET_OK ){
22627 			++pMap->iNextIdx;
22628 		}
22629 	}
22630 	/* Insertion result */
22631 	return rc;
22632 }
22633 /*
22634  * Extract node value.
22635  */
HashmapExtractNodeValue(jx9_hashmap_node * pNode)22636 static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode)
22637 {
22638 	/* Point to the desired object */
22639 	jx9_value *pObj;
22640 	pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
22641 	return pObj;
22642 }
22643 /*
22644  * Insert a node in the given hashmap.
22645  * If a node with the given key already exists in the database
22646  * then this function overwrite the old value.
22647  */
HashmapInsertNode(jx9_hashmap * pMap,jx9_hashmap_node * pNode,int bPreserve)22648 static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve)
22649 {
22650 	jx9_value *pObj;
22651 	sxi32 rc;
22652 	/* Extract the node value */
22653 	pObj = HashmapExtractNodeValue(&(*pNode));
22654 	if( pObj == 0 ){
22655 		return SXERR_EMPTY;
22656 	}
22657 	/* Preserve key */
22658 	if( pNode->iType == HASHMAP_INT_NODE){
22659 		/* Int64 key */
22660 		if( !bPreserve ){
22661 			/* Assign an automatic index */
22662 			rc = HashmapInsert(&(*pMap), 0, pObj);
22663 		}else{
22664 			rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj);
22665 		}
22666 	}else{
22667 		/* Blob key */
22668 		rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
22669 			SyBlobLength(&pNode->xKey.sKey), pObj);
22670 	}
22671 	return rc;
22672 }
22673 /*
22674  * Compare two node values.
22675  * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
22676  * or < 0 if pRight is greater than pLeft.
22677  * For a full description on jx9_values comparison, refer to the implementation
22678  * of the [jx9MemObjCmp()] function defined in memobj.c or the official
22679  * documenation.
22680  */
HashmapNodeCmp(jx9_hashmap_node * pLeft,jx9_hashmap_node * pRight,int bStrict)22681 static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict)
22682 {
22683 	jx9_value sObj1, sObj2;
22684 	sxi32 rc;
22685 	if( pLeft == pRight ){
22686 		/*
22687 		 * Same node.Refer to the sort() implementation defined
22688 		 * below for more information on this sceanario.
22689 		 */
22690 		return 0;
22691 	}
22692 	/* Do the comparison */
22693 	jx9MemObjInit(pLeft->pMap->pVm, &sObj1);
22694 	jx9MemObjInit(pLeft->pMap->pVm, &sObj2);
22695 	jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
22696 	jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE);
22697 	rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0);
22698 	jx9MemObjRelease(&sObj1);
22699 	jx9MemObjRelease(&sObj2);
22700 	return rc;
22701 }
22702 /*
22703  * Rehash a node with a 64-bit integer key.
22704  * Refer to [merge_sort(), array_shift()] implementations for more information.
22705  */
HashmapRehashIntNode(jx9_hashmap_node * pEntry)22706 static void HashmapRehashIntNode(jx9_hashmap_node *pEntry)
22707 {
22708 	jx9_hashmap *pMap = pEntry->pMap;
22709 	sxu32 nBucket;
22710 	/* Remove old collision links */
22711 	if( pEntry->pPrevCollide ){
22712 		pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
22713 	}else{
22714 		pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
22715 	}
22716 	if( pEntry->pNextCollide ){
22717 		pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
22718 	}
22719 	pEntry->pNextCollide = pEntry->pPrevCollide = 0;
22720 	/* Compute the new hash */
22721 	pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
22722 	pEntry->xKey.iKey = pMap->iNextIdx;
22723 	nBucket = pEntry->nHash & (pMap->nSize - 1);
22724 	/* Link to the new bucket */
22725 	pEntry->pNextCollide = pMap->apBucket[nBucket];
22726 	if( pMap->apBucket[nBucket] ){
22727 		pMap->apBucket[nBucket]->pPrevCollide = pEntry;
22728 	}
22729 	pEntry->pNextCollide = pMap->apBucket[nBucket];
22730 	pMap->apBucket[nBucket] = pEntry;
22731 	/* Increment the automatic index */
22732 	pMap->iNextIdx++;
22733 }
22734 /*
22735  * Perform a linear search on a given hashmap.
22736  * Write a pointer to the target node on success.
22737  * Otherwise SXERR_NOTFOUND is returned on failure.
22738  * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations
22739  * for more information.
22740  */
HashmapFindValue(jx9_hashmap * pMap,jx9_value * pNeedle,jx9_hashmap_node ** ppNode,int bStrict)22741 static int HashmapFindValue(
22742 	jx9_hashmap *pMap,   /* Target hashmap */
22743 	jx9_value *pNeedle,  /* Lookup key */
22744 	jx9_hashmap_node **ppNode, /* OUT: target node on success  */
22745 	int bStrict      /* TRUE for strict comparison */
22746 	)
22747 {
22748 	jx9_hashmap_node *pEntry;
22749 	jx9_value sVal, *pVal;
22750 	jx9_value sNeedle;
22751 	sxi32 rc;
22752 	sxu32 n;
22753 	/* Perform a linear search since we cannot sort the hashmap based on values */
22754 	pEntry = pMap->pFirst;
22755 	n = pMap->nEntry;
22756 	jx9MemObjInit(pMap->pVm, &sVal);
22757 	jx9MemObjInit(pMap->pVm, &sNeedle);
22758 	for(;;){
22759 		if( n < 1 ){
22760 			break;
22761 		}
22762 		/* Extract node value */
22763 		pVal = HashmapExtractNodeValue(pEntry);
22764 		if( pVal ){
22765 			if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){
22766 				sxi32 iF1 = pVal->iFlags;
22767 				sxi32 iF2 = pNeedle->iFlags;
22768 				if( iF1 == iF2 ){
22769 					/* NULL values are equals */
22770 					if( ppNode ){
22771 						*ppNode = pEntry;
22772 					}
22773 					return SXRET_OK;
22774 				}
22775 			}else{
22776 				/* Duplicate value */
22777 				jx9MemObjLoad(pVal, &sVal);
22778 				jx9MemObjLoad(pNeedle, &sNeedle);
22779 				rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0);
22780 				jx9MemObjRelease(&sVal);
22781 				jx9MemObjRelease(&sNeedle);
22782 				if( rc == 0 ){
22783 					if( ppNode ){
22784 						*ppNode = pEntry;
22785 					}
22786 					/* Match found*/
22787 					return SXRET_OK;
22788 				}
22789 			}
22790 		}
22791 		/* Point to the next entry */
22792 		pEntry = pEntry->pPrev; /* Reverse link */
22793 		n--;
22794 	}
22795 	/* No such entry */
22796 	return SXERR_NOTFOUND;
22797 }
22798 /*
22799  * Compare two hashmaps.
22800  * Return 0 if the hashmaps are equals.Any other value indicates inequality.
22801  * Note on array comparison operators.
22802  *  According to the JX9 language reference manual.
22803  *  Array Operators Example 	Name 	Result
22804  *  $a + $b 	Union 	Union of $a and $b.
22805  *  $a == $b 	Equality 	TRUE if $a and $b have the same key/value pairs.
22806  *  $a === $b 	Identity 	TRUE if $a and $b have the same key/value pairs in the same
22807  *                          order and of the same types.
22808  *  $a != $b 	Inequality 	TRUE if $a is not equal to $b.
22809  *  $a <> $b 	Inequality 	TRUE if $a is not equal to $b.
22810  *  $a !== $b 	Non-identity 	TRUE if $a is not identical to $b.
22811  * The + operator returns the right-hand array appended to the left-hand array;
22812  * For keys that exist in both arrays, the elements from the left-hand array will be used
22813  * and the matching elements from the right-hand array will be ignored.
22814  * <?jx9
22815  * $a = array("a" => "apple", "b" => "banana");
22816  * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
22817  * $c = $a + $b; // Union of $a and $b
22818  * print "Union of \$a and \$b: \n";
22819  * dump($c);
22820  * $c = $b + $a; // Union of $b and $a
22821  * print "Union of \$b and \$a: \n";
22822  * dump($c);
22823  * ?>
22824  * When executed, this script will print the following:
22825  * Union of $a and $b:
22826  * array(3) {
22827  *  ["a"]=>
22828  *  string(5) "apple"
22829  *  ["b"]=>
22830  * string(6) "banana"
22831  *  ["c"]=>
22832  * string(6) "cherry"
22833  * }
22834  * Union of $b and $a:
22835  * array(3) {
22836  * ["a"]=>
22837  * string(4) "pear"
22838  * ["b"]=>
22839  * string(10) "strawberry"
22840  * ["c"]=>
22841  * string(6) "cherry"
22842  * }
22843  * Elements of arrays are equal for the comparison if they have the same key and value.
22844  */
jx9HashmapCmp(jx9_hashmap * pLeft,jx9_hashmap * pRight,int bStrict)22845 JX9_PRIVATE sxi32 jx9HashmapCmp(
22846 	jx9_hashmap *pLeft,  /* Left hashmap */
22847 	jx9_hashmap *pRight, /* Right hashmap */
22848 	int bStrict          /* TRUE for strict comparison */
22849 	)
22850 {
22851 	jx9_hashmap_node *pLe, *pRe;
22852 	sxi32 rc;
22853 	sxu32 n;
22854 	if( pLeft == pRight ){
22855 		/* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
22856 		 * Unlike the  engine.
22857 		 */
22858 		return 0;
22859 	}
22860 	if( pLeft->nEntry != pRight->nEntry ){
22861 		/* Must have the same number of entries */
22862 		return pLeft->nEntry > pRight->nEntry ? 1 : -1;
22863 	}
22864 	/* Point to the first inserted entry of the left hashmap */
22865 	pLe = pLeft->pFirst;
22866 	pRe = 0; /* cc warning */
22867 	/* Perform the comparison */
22868 	n = pLeft->nEntry;
22869 	for(;;){
22870 		if( n < 1 ){
22871 			break;
22872 		}
22873 		if( pLe->iType == HASHMAP_INT_NODE){
22874 			/* Int key */
22875 			rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
22876 		}else{
22877 			SyBlob *pKey = &pLe->xKey.sKey;
22878 			/* Blob key */
22879 			rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
22880 		}
22881 		if( rc != SXRET_OK ){
22882 			/* No such entry in the right side */
22883 			return 1;
22884 		}
22885 		rc = 0;
22886 		if( bStrict ){
22887 			/* Make sure, the keys are of the same type */
22888 			if( pLe->iType != pRe->iType ){
22889 				rc = 1;
22890 			}
22891 		}
22892 		if( !rc ){
22893 			/* Compare nodes */
22894 			rc = HashmapNodeCmp(pLe, pRe, bStrict);
22895 		}
22896 		if( rc != 0 ){
22897 			/* Nodes key/value differ */
22898 			return rc;
22899 		}
22900 		/* Point to the next entry */
22901 		pLe = pLe->pPrev; /* Reverse link */
22902 		n--;
22903 	}
22904 	return 0; /* Hashmaps are equals */
22905 }
22906 /*
22907  * Merge two hashmaps.
22908  * Note on the merge process
22909  * According to the JX9 language reference manual.
22910  *  Merges the elements of two arrays together so that the values of one are appended
22911  *  to the end of the previous one. It returns the resulting array (pDest).
22912  *  If the input arrays have the same string keys, then the later value for that key
22913  *  will overwrite the previous one. If, however, the arrays contain numeric keys
22914  *  the later value will not overwrite the original value, but will be appended.
22915  *  Values in the input array with numeric keys will be renumbered with incrementing
22916  *  keys starting from zero in the result array.
22917  */
HashmapMerge(jx9_hashmap * pSrc,jx9_hashmap * pDest)22918 static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest)
22919 {
22920 	jx9_hashmap_node *pEntry;
22921 	jx9_value sKey, *pVal;
22922 	sxi32 rc;
22923 	sxu32 n;
22924 	if( pSrc == pDest ){
22925 		/* Same map. This can easily happen since hashmaps are passed by reference.
22926 		 * Unlike the  engine.
22927 		 */
22928 		return SXRET_OK;
22929 	}
22930 	/* Point to the first inserted entry in the source */
22931 	pEntry = pSrc->pFirst;
22932 	/* Perform the merge */
22933 	for( n = 0 ; n < pSrc->nEntry ; ++n ){
22934 		/* Extract the node value */
22935 		pVal = HashmapExtractNodeValue(pEntry);
22936 		if( pEntry->iType == HASHMAP_BLOB_NODE ){
22937 			/* Blob key insertion */
22938 			jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
22939 			jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
22940 			rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
22941 			jx9MemObjRelease(&sKey);
22942 		}else{
22943 			rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
22944 		}
22945 		if( rc != SXRET_OK ){
22946 			return rc;
22947 		}
22948 		/* Point to the next entry */
22949 		pEntry = pEntry->pPrev; /* Reverse link */
22950 	}
22951 	return SXRET_OK;
22952 }
22953 /*
22954  * Duplicate the contents of a hashmap. Store the copy in pDest.
22955  * Refer to the [array_pad(), array_copy(), ...] implementation for more information.
22956  */
jx9HashmapDup(jx9_hashmap * pSrc,jx9_hashmap * pDest)22957 JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest)
22958 {
22959 	jx9_hashmap_node *pEntry;
22960 	jx9_value sKey, *pVal;
22961 	sxi32 rc;
22962 	sxu32 n;
22963 	if( pSrc == pDest ){
22964 		/* Same map. This can easily happen since hashmaps are passed by reference.
22965 		 * Unlike the  engine.
22966 		 */
22967 		return SXRET_OK;
22968 	}
22969 	/* Point to the first inserted entry in the source */
22970 	pEntry = pSrc->pFirst;
22971 	/* Perform the duplication */
22972 	for( n = 0 ; n < pSrc->nEntry ; ++n ){
22973 		/* Extract the node value */
22974 		pVal = HashmapExtractNodeValue(pEntry);
22975 		if( pEntry->iType == HASHMAP_BLOB_NODE ){
22976 			/* Blob key insertion */
22977 			jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
22978 			jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
22979 			rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
22980 			jx9MemObjRelease(&sKey);
22981 		}else{
22982 			/* Int key insertion */
22983 			rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal);
22984 		}
22985 		if( rc != SXRET_OK ){
22986 			return rc;
22987 		}
22988 		/* Point to the next entry */
22989 		pEntry = pEntry->pPrev; /* Reverse link */
22990 	}
22991 	return SXRET_OK;
22992 }
22993 /*
22994  * Perform the union of two hashmaps.
22995  * This operation is performed only if the user uses the '+' operator
22996  * with a variable holding an array as follows:
22997  * <?jx9
22998  * $a = array("a" => "apple", "b" => "banana");
22999  * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
23000  * $c = $a + $b; // Union of $a and $b
23001  * print "Union of \$a and \$b: \n";
23002  * dump($c);
23003  * $c = $b + $a; // Union of $b and $a
23004  * print "Union of \$b and \$a: \n";
23005  * dump($c);
23006  * ?>
23007  * When executed, this script will print the following:
23008  * Union of $a and $b:
23009  * array(3) {
23010  *  ["a"]=>
23011  *  string(5) "apple"
23012  *  ["b"]=>
23013  * string(6) "banana"
23014  *  ["c"]=>
23015  * string(6) "cherry"
23016  * }
23017  * Union of $b and $a:
23018  * array(3) {
23019  * ["a"]=>
23020  * string(4) "pear"
23021  * ["b"]=>
23022  * string(10) "strawberry"
23023  * ["c"]=>
23024  * string(6) "cherry"
23025  * }
23026  * The + operator returns the right-hand array appended to the left-hand array;
23027  * For keys that exist in both arrays, the elements from the left-hand array will be used
23028  * and the matching elements from the right-hand array will be ignored.
23029  */
jx9HashmapUnion(jx9_hashmap * pLeft,jx9_hashmap * pRight)23030 JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight)
23031 {
23032 	jx9_hashmap_node *pEntry;
23033 	sxi32 rc = SXRET_OK;
23034 	jx9_value *pObj;
23035 	sxu32 n;
23036 	if( pLeft == pRight ){
23037 		/* Same map. This can easily happen since hashmaps are passed by reference.
23038 		 * Unlike the  engine.
23039 		 */
23040 		return SXRET_OK;
23041 	}
23042 	/* Perform the union */
23043 	pEntry = pRight->pFirst;
23044 	for(n = 0 ; n < pRight->nEntry ; ++n ){
23045 		/* Make sure the given key does not exists in the left array */
23046 		if( pEntry->iType == HASHMAP_BLOB_NODE ){
23047 			/* BLOB key */
23048 			if( SXRET_OK !=
23049 				HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){
23050 					pObj = HashmapExtractNodeValue(pEntry);
23051 					if( pObj ){
23052 						/* Perform the insertion */
23053 						rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey),
23054 							SyBlobLength(&pEntry->xKey.sKey),pObj);
23055 						if( rc != SXRET_OK ){
23056 							return rc;
23057 						}
23058 					}
23059 			}
23060 		}else{
23061 			/* INT key */
23062 			if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){
23063 				pObj = HashmapExtractNodeValue(pEntry);
23064 				if( pObj ){
23065 					/* Perform the insertion */
23066 					rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj);
23067 					if( rc != SXRET_OK ){
23068 						return rc;
23069 					}
23070 				}
23071 			}
23072 		}
23073 		/* Point to the next entry */
23074 		pEntry = pEntry->pPrev; /* Reverse link */
23075 	}
23076 	return SXRET_OK;
23077 }
23078 /*
23079  * Allocate a new hashmap.
23080  * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
23081  */
jx9NewHashmap(jx9_vm * pVm,sxu32 (* xIntHash)(sxi64),sxu32 (* xBlobHash)(const void *,sxu32))23082 JX9_PRIVATE jx9_hashmap * jx9NewHashmap(
23083 	jx9_vm *pVm,              /* VM that trigger the hashmap creation */
23084 	sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/
23085 	sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
23086 	)
23087 {
23088 	jx9_hashmap *pMap;
23089 	/* Allocate a new instance */
23090 	pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap));
23091 	if( pMap == 0 ){
23092 		return 0;
23093 	}
23094 	/* Zero the structure */
23095 	SyZero(pMap, sizeof(jx9_hashmap));
23096 	/* Fill in the structure */
23097 	pMap->pVm = &(*pVm);
23098 	pMap->iRef = 1;
23099 	/* pMap->iFlags = 0; */
23100 	/* Default hash functions */
23101 	pMap->xIntHash  = xIntHash ? xIntHash : IntHash;
23102 	pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
23103 	return pMap;
23104 }
23105 /*
23106  * Install superglobals in the given virtual machine.
23107  * Note on superglobals.
23108  *  According to the JX9 language reference manual.
23109  *  Superglobals are built-in variables that are always available in all scopes.
23110 *   Description
23111 *   All predefined variables in JX9 are "superglobals", which means they
23112 *   are available in all scopes throughout a script.
23113 *   These variables are:
23114 *    $_SERVER
23115 *    $_GET
23116 *    $_POST
23117 *    $_FILES
23118 *    $_REQUEST
23119 *    $_ENV
23120 */
jx9HashmapLoadBuiltin(jx9_vm * pVm)23121 JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm)
23122 {
23123 	static const char * azSuper[] = {
23124 		"_SERVER",   /* $_SERVER */
23125 		"_GET",      /* $_GET */
23126 		"_POST",     /* $_POST */
23127 		"_FILES",    /* $_FILES */
23128 		"_REQUEST",  /* $_REQUEST */
23129 		"_COOKIE",   /* $_COOKIE */
23130 		"_ENV",      /* $_ENV */
23131 		"_HEADER",   /* $_HEADER */
23132 		"argv"       /* $argv */
23133 	};
23134 	SyString *pFile;
23135 	sxi32 rc;
23136 	sxu32 n;
23137 	/* Install globals variable now */
23138 	for( n =  0 ; n < SX_ARRAYSIZE(azSuper)  ; n++ ){
23139 		jx9_value *pSuper;
23140 		/* Request an empty array */
23141 		pSuper = jx9_new_array(&(*pVm));
23142 		if( pSuper == 0 ){
23143 			return SXERR_MEM;
23144 		}
23145 		/* Install */
23146 		rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
23147 		if( rc != SXRET_OK ){
23148 			return rc;
23149 		}
23150 		/* Release the value now it have been installed */
23151 		jx9_release_value(&(*pVm), pSuper);
23152 	}
23153 	/* Set some $_SERVER entries */
23154 	pFile = (SyString *)SySetPeek(&pVm->aFiles);
23155 	/*
23156 	 * 'SCRIPT_FILENAME'
23157 	 * The absolute pathname of the currently executing script.
23158 	 */
23159 	jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR,
23160 		"SCRIPT_FILENAME",
23161 		pFile ? pFile->zString : ":Memory:",
23162 		pFile ? pFile->nByte : sizeof(":Memory:") - 1
23163 		);
23164 	/* All done, all global variables are installed now */
23165 	return SXRET_OK;
23166 }
23167 /*
23168  * Release a hashmap.
23169  */
jx9HashmapRelease(jx9_hashmap * pMap,int FreeDS)23170 JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS)
23171 {
23172 	jx9_hashmap_node *pEntry, *pNext;
23173 	jx9_vm *pVm = pMap->pVm;
23174 	sxu32 n;
23175 	/* Start the release process */
23176 	n = 0;
23177 	pEntry = pMap->pFirst;
23178 	for(;;){
23179 		if( n >= pMap->nEntry ){
23180 			break;
23181 		}
23182 		pNext = pEntry->pPrev; /* Reverse link */
23183 		/* Restore the jx9_value to the free list */
23184 		jx9VmUnsetMemObj(pVm, pEntry->nValIdx);
23185 		/* Release the node */
23186 		if( pEntry->iType == HASHMAP_BLOB_NODE ){
23187 			SyBlobRelease(&pEntry->xKey.sKey);
23188 		}
23189 		SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
23190 		/* Point to the next entry */
23191 		pEntry = pNext;
23192 		n++;
23193 	}
23194 	if( pMap->nEntry > 0 ){
23195 		/* Release the hash bucket */
23196 		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
23197 	}
23198 	if( FreeDS ){
23199 		/* Free the whole instance */
23200 		SyMemBackendPoolFree(&pVm->sAllocator, pMap);
23201 	}else{
23202 		/* Keep the instance but reset it's fields */
23203 		pMap->apBucket = 0;
23204 		pMap->iNextIdx = 0;
23205 		pMap->nEntry = pMap->nSize = 0;
23206 		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
23207 	}
23208 	return SXRET_OK;
23209 }
23210 /*
23211  * Decrement the reference count of a given hashmap.
23212  * If the count reaches zero which mean no more variables
23213  * are pointing to this hashmap, then release the whole instance.
23214  */
jx9HashmapUnref(jx9_hashmap * pMap)23215 JX9_PRIVATE void  jx9HashmapUnref(jx9_hashmap *pMap)
23216 {
23217 	pMap->iRef--;
23218 	if( pMap->iRef < 1 ){
23219 		jx9HashmapRelease(pMap, TRUE);
23220 	}
23221 }
23222 /*
23223  * Check if a given key exists in the given hashmap.
23224  * Write a pointer to the target node on success.
23225  * Otherwise SXERR_NOTFOUND is returned on failure.
23226  */
jx9HashmapLookup(jx9_hashmap * pMap,jx9_value * pKey,jx9_hashmap_node ** ppNode)23227 JX9_PRIVATE sxi32 jx9HashmapLookup(
23228 	jx9_hashmap *pMap,        /* Target hashmap */
23229 	jx9_value *pKey,          /* Lookup key */
23230 	jx9_hashmap_node **ppNode /* OUT: Target node on success */
23231 	)
23232 {
23233 	sxi32 rc;
23234 	if( pMap->nEntry < 1 ){
23235 		/* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway.
23236 		 */
23237 		return SXERR_NOTFOUND;
23238 	}
23239 	rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
23240 	return rc;
23241 }
23242 /*
23243  * Insert a given key and it's associated value (if any) in the given
23244  * hashmap.
23245  * If a node with the given key already exists in the database
23246  * then this function overwrite the old value.
23247  */
jx9HashmapInsert(jx9_hashmap * pMap,jx9_value * pKey,jx9_value * pVal)23248 JX9_PRIVATE sxi32 jx9HashmapInsert(
23249 	jx9_hashmap *pMap, /* Target hashmap */
23250 	jx9_value *pKey,   /* Lookup key */
23251 	jx9_value *pVal    /* Node value.NULL otherwise */
23252 	)
23253 {
23254 	sxi32 rc;
23255 	rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
23256 	return rc;
23257 }
23258 /*
23259  * Reset the node cursor of a given hashmap.
23260  */
jx9HashmapResetLoopCursor(jx9_hashmap * pMap)23261 JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap)
23262 {
23263 	/* Reset the loop cursor */
23264 	pMap->pCur = pMap->pFirst;
23265 }
23266 /*
23267  * Return a pointer to the node currently pointed by the node cursor.
23268  * If the cursor reaches the end of the list, then this function
23269  * return NULL.
23270  * Note that the node cursor is automatically advanced by this function.
23271  */
jx9HashmapGetNextEntry(jx9_hashmap * pMap)23272 JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap)
23273 {
23274 	jx9_hashmap_node *pCur = pMap->pCur;
23275 	if( pCur == 0 ){
23276 		/* End of the list, return null */
23277 		return 0;
23278 	}
23279 	/* Advance the node cursor */
23280 	pMap->pCur = pCur->pPrev; /* Reverse link */
23281 	return pCur;
23282 }
23283 /*
23284  * Extract a node value.
23285  */
jx9HashmapGetNodeValue(jx9_hashmap_node * pNode)23286 JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode)
23287 {
23288 	jx9_value *pValue;
23289 	pValue = HashmapExtractNodeValue(pNode);
23290 	return pValue;
23291 }
23292 /*
23293  * Extract a node value (Second).
23294  */
jx9HashmapExtractNodeValue(jx9_hashmap_node * pNode,jx9_value * pValue,int bStore)23295 JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore)
23296 {
23297 	jx9_value *pEntry = HashmapExtractNodeValue(pNode);
23298 	if( pEntry ){
23299 		if( bStore ){
23300 			jx9MemObjStore(pEntry, pValue);
23301 		}else{
23302 			jx9MemObjLoad(pEntry, pValue);
23303 		}
23304 	}else{
23305 		jx9MemObjRelease(pValue);
23306 	}
23307 }
23308 /*
23309  * Extract a node key.
23310  */
jx9HashmapExtractNodeKey(jx9_hashmap_node * pNode,jx9_value * pKey)23311 JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey)
23312 {
23313 	/* Fill with the current key */
23314 	if( pNode->iType == HASHMAP_INT_NODE ){
23315 		if( SyBlobLength(&pKey->sBlob) > 0 ){
23316 			SyBlobRelease(&pKey->sBlob);
23317 		}
23318 		pKey->x.iVal = pNode->xKey.iKey;
23319 		MemObjSetType(pKey, MEMOBJ_INT);
23320 	}else{
23321 		SyBlobReset(&pKey->sBlob);
23322 		SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
23323 		MemObjSetType(pKey, MEMOBJ_STRING);
23324 	}
23325 }
23326 #ifndef JX9_DISABLE_BUILTIN_FUNC
23327 /*
23328  * Store the address of nodes value in the given container.
23329  * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations
23330  * defined in 'builtin.c' for more information.
23331  */
jx9HashmapValuesToSet(jx9_hashmap * pMap,SySet * pOut)23332 JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut)
23333 {
23334 	jx9_hashmap_node *pEntry = pMap->pFirst;
23335 	jx9_value *pValue;
23336 	sxu32 n;
23337 	/* Initialize the container */
23338 	SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *));
23339 	for(n = 0 ; n < pMap->nEntry ; n++ ){
23340 		/* Extract node value */
23341 		pValue = HashmapExtractNodeValue(pEntry);
23342 		if( pValue ){
23343 			SySetPut(pOut, (const void *)&pValue);
23344 		}
23345 		/* Point to the next entry */
23346 		pEntry = pEntry->pPrev; /* Reverse link */
23347 	}
23348 	/* Total inserted entries */
23349 	return (int)SySetUsed(pOut);
23350 }
23351 #endif /* JX9_DISABLE_BUILTIN_FUNC */
23352 /*
23353  * Merge sort.
23354  * The merge sort implementation is based on the one found in the SQLite3 source tree.
23355  * Status: Public domain
23356  */
23357 /* Node comparison callback signature */
23358 typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *);
23359 /*
23360 ** Inputs:
23361 **   a:       A sorted, null-terminated linked list.  (May be null).
23362 **   b:       A sorted, null-terminated linked list.  (May be null).
23363 **   cmp:     A pointer to the comparison function.
23364 **
23365 ** Return Value:
23366 **   A pointer to the head of a sorted list containing the elements
23367 **   of both a and b.
23368 **
23369 ** Side effects:
23370 **   The "next", "prev" pointers for elements in the lists a and b are
23371 **   changed.
23372 */
HashmapNodeMerge(jx9_hashmap_node * pA,jx9_hashmap_node * pB,ProcNodeCmp xCmp,void * pCmpData)23373 static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData)
23374 {
23375 	jx9_hashmap_node result, *pTail;
23376     /* Prevent compiler warning */
23377 	result.pNext = result.pPrev = 0;
23378 	pTail = &result;
23379 	while( pA && pB ){
23380 		if( xCmp(pA, pB, pCmpData) < 0 ){
23381 			pTail->pPrev = pA;
23382 			pA->pNext = pTail;
23383 			pTail = pA;
23384 			pA = pA->pPrev;
23385 		}else{
23386 			pTail->pPrev = pB;
23387 			pB->pNext = pTail;
23388 			pTail = pB;
23389 			pB = pB->pPrev;
23390 		}
23391 	}
23392 	if( pA ){
23393 		pTail->pPrev = pA;
23394 		pA->pNext = pTail;
23395 	}else if( pB ){
23396 		pTail->pPrev = pB;
23397 		pB->pNext = pTail;
23398 	}else{
23399 		pTail->pPrev = pTail->pNext = 0;
23400 	}
23401 	return result.pPrev;
23402 }
23403 /*
23404 ** Inputs:
23405 **   Map:       Input hashmap
23406 **   cmp:       A comparison function.
23407 **
23408 ** Return Value:
23409 **   Sorted hashmap.
23410 **
23411 ** Side effects:
23412 **   The "next" pointers for elements in list are changed.
23413 */
23414 #define N_SORT_BUCKET  32
HashmapMergeSort(jx9_hashmap * pMap,ProcNodeCmp xCmp,void * pCmpData)23415 static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData)
23416 {
23417 	jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
23418 	sxu32 i;
23419 	SyZero(a, sizeof(a));
23420 	/* Point to the first inserted entry */
23421 	pIn = pMap->pFirst;
23422 	while( pIn ){
23423 		p = pIn;
23424 		pIn = p->pPrev;
23425 		p->pPrev = 0;
23426 		for(i=0; i<N_SORT_BUCKET-1; i++){
23427 			if( a[i]==0 ){
23428 				a[i] = p;
23429 				break;
23430 			}else{
23431 				p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
23432 				a[i] = 0;
23433 			}
23434 		}
23435 		if( i==N_SORT_BUCKET-1 ){
23436 			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
23437 			 * But that is impossible.
23438 			 */
23439 			a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
23440 		}
23441 	}
23442 	p = a[0];
23443 	for(i=1; i<N_SORT_BUCKET; i++){
23444 		p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
23445 	}
23446 	p->pNext = 0;
23447 	/* Reflect the change */
23448 	pMap->pFirst = p;
23449 	/* Reset the loop cursor */
23450 	pMap->pCur = pMap->pFirst;
23451 	return SXRET_OK;
23452 }
23453 /*
23454  * Node comparison callback.
23455  * used-by: [sort(), asort(), ...]
23456  */
HashmapCmpCallback1(jx9_hashmap_node * pA,jx9_hashmap_node * pB,void * pCmpData)23457 static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23458 {
23459 	jx9_value sA, sB;
23460 	sxi32 iFlags;
23461 	int rc;
23462 	if( pCmpData == 0 ){
23463 		/* Perform a standard comparison */
23464 		rc = HashmapNodeCmp(pA, pB, FALSE);
23465 		return rc;
23466 	}
23467 	iFlags = SX_PTR_TO_INT(pCmpData);
23468 	/* Duplicate node values */
23469 	jx9MemObjInit(pA->pMap->pVm, &sA);
23470 	jx9MemObjInit(pA->pMap->pVm, &sB);
23471 	jx9HashmapExtractNodeValue(pA, &sA, FALSE);
23472 	jx9HashmapExtractNodeValue(pB, &sB, FALSE);
23473 	if( iFlags == 5 ){
23474 		/* String cast */
23475 		if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
23476 			jx9MemObjToString(&sA);
23477 		}
23478 		if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
23479 			jx9MemObjToString(&sB);
23480 		}
23481 	}else{
23482 		/* Numeric cast */
23483 		jx9MemObjToNumeric(&sA);
23484 		jx9MemObjToNumeric(&sB);
23485 	}
23486 	/* Perform the comparison */
23487 	rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
23488 	jx9MemObjRelease(&sA);
23489 	jx9MemObjRelease(&sB);
23490 	return rc;
23491 }
23492 /*
23493  * Node comparison callback.
23494  * Used by: [rsort(), arsort()];
23495  */
HashmapCmpCallback3(jx9_hashmap_node * pA,jx9_hashmap_node * pB,void * pCmpData)23496 static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23497 {
23498 	jx9_value sA, sB;
23499 	sxi32 iFlags;
23500 	int rc;
23501 	if( pCmpData == 0 ){
23502 		/* Perform a standard comparison */
23503 		rc = HashmapNodeCmp(pA, pB, FALSE);
23504 		return -rc;
23505 	}
23506 	iFlags = SX_PTR_TO_INT(pCmpData);
23507 	/* Duplicate node values */
23508 	jx9MemObjInit(pA->pMap->pVm, &sA);
23509 	jx9MemObjInit(pA->pMap->pVm, &sB);
23510 	jx9HashmapExtractNodeValue(pA, &sA, FALSE);
23511 	jx9HashmapExtractNodeValue(pB, &sB, FALSE);
23512 	if( iFlags == 5 ){
23513 		/* String cast */
23514 		if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
23515 			jx9MemObjToString(&sA);
23516 		}
23517 		if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
23518 			jx9MemObjToString(&sB);
23519 		}
23520 	}else{
23521 		/* Numeric cast */
23522 		jx9MemObjToNumeric(&sA);
23523 		jx9MemObjToNumeric(&sB);
23524 	}
23525 	/* Perform the comparison */
23526 	rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
23527 	jx9MemObjRelease(&sA);
23528 	jx9MemObjRelease(&sB);
23529 	return -rc;
23530 }
23531 /*
23532  * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
23533  * used-by: [usort(), uasort()]
23534  */
HashmapCmpCallback4(jx9_hashmap_node * pA,jx9_hashmap_node * pB,void * pCmpData)23535 static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
23536 {
23537 	jx9_value sResult, *pCallback;
23538 	jx9_value *pV1, *pV2;
23539 	jx9_value *apArg[2];  /* Callback arguments */
23540 	sxi32 rc;
23541 	/* Point to the desired callback */
23542 	pCallback = (jx9_value *)pCmpData;
23543 	/* initialize the result value */
23544 	jx9MemObjInit(pA->pMap->pVm, &sResult);
23545 	/* Extract nodes values */
23546 	pV1 = HashmapExtractNodeValue(pA);
23547 	pV2 = HashmapExtractNodeValue(pB);
23548 	apArg[0] = pV1;
23549 	apArg[1] = pV2;
23550 	/* Invoke the callback */
23551 	rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
23552 	if( rc != SXRET_OK ){
23553 		/* An error occured while calling user defined function [i.e: not defined] */
23554 		rc = -1; /* Set a dummy result */
23555 	}else{
23556 		/* Extract callback result */
23557 		if((sResult.iFlags & MEMOBJ_INT) == 0 ){
23558 			/* Perform an int cast */
23559 			jx9MemObjToInteger(&sResult);
23560 		}
23561 		rc = (sxi32)sResult.x.iVal;
23562 	}
23563 	jx9MemObjRelease(&sResult);
23564 	/* Callback result */
23565 	return rc;
23566 }
23567 /*
23568  * Rehash all nodes keys after a merge-sort have been applied.
23569  * Used by [sort(), usort() and rsort()].
23570  */
HashmapSortRehash(jx9_hashmap * pMap)23571 static void HashmapSortRehash(jx9_hashmap *pMap)
23572 {
23573 	jx9_hashmap_node *p, *pLast;
23574 	sxu32 i;
23575 	/* Rehash all entries */
23576 	pLast = p = pMap->pFirst;
23577 	pMap->iNextIdx = 0; /* Reset the automatic index */
23578 	i = 0;
23579 	for( ;; ){
23580 		if( i >= pMap->nEntry ){
23581 			pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
23582 			break;
23583 		}
23584 		if( p->iType == HASHMAP_BLOB_NODE ){
23585 			/* Do not maintain index association as requested by the JX9 specification */
23586 			SyBlobRelease(&p->xKey.sKey);
23587 			/* Change key type */
23588 			p->iType = HASHMAP_INT_NODE;
23589 		}
23590 		HashmapRehashIntNode(p);
23591 		/* Point to the next entry */
23592 		i++;
23593 		pLast = p;
23594 		p = p->pPrev; /* Reverse link */
23595 	}
23596 }
23597 /*
23598  * Array functions implementation.
23599  * Authors:
23600  *  Symisc Systems, devel@symisc.net.
23601  *  Copyright (C) Symisc Systems, http://jx9.symisc.net
23602  * Status:
23603  *  Stable.
23604  */
23605 /*
23606  * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] )
23607  * Sort an array.
23608  * Parameters
23609  *  $array
23610  *   The input array.
23611  * $sort_flags
23612  *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
23613  *  Sorting type flags:
23614  *   SORT_REGULAR - compare items normally (don't change types)
23615  *   SORT_NUMERIC - compare items numerically
23616  *   SORT_STRING - compare items as strings
23617  * Return
23618  *  TRUE on success or FALSE on failure.
23619  *
23620  */
jx9_hashmap_sort(jx9_context * pCtx,int nArg,jx9_value ** apArg)23621 static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23622 {
23623 	jx9_hashmap *pMap;
23624 	/* Make sure we are dealing with a valid hashmap */
23625 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23626 		/* Missing/Invalid arguments, return FALSE */
23627 		jx9_result_bool(pCtx, 0);
23628 		return JX9_OK;
23629 	}
23630 	/* Point to the internal representation of the input hashmap */
23631 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23632 	if( pMap->nEntry > 1 ){
23633 		sxi32 iCmpFlags = 0;
23634 		if( nArg > 1 ){
23635 			/* Extract comparison flags */
23636 			iCmpFlags = jx9_value_to_int(apArg[1]);
23637 			if( iCmpFlags == 3 /* SORT_REGULAR */ ){
23638 				iCmpFlags = 0; /* Standard comparison */
23639 			}
23640 		}
23641 		/* Do the merge sort */
23642 		HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
23643 		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
23644 		HashmapSortRehash(pMap);
23645 	}
23646 	/* All done, return TRUE */
23647 	jx9_result_bool(pCtx, 1);
23648 	return JX9_OK;
23649 }
23650 /*
23651  * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] )
23652  * Sort an array in reverse order.
23653  * Parameters
23654  *  $array
23655  *   The input array.
23656  * $sort_flags
23657  *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
23658  *  Sorting type flags:
23659  *   SORT_REGULAR - compare items normally (don't change types)
23660  *   SORT_NUMERIC - compare items numerically
23661  *   SORT_STRING - compare items as strings
23662  * Return
23663  *  TRUE on success or FALSE on failure.
23664  */
jx9_hashmap_rsort(jx9_context * pCtx,int nArg,jx9_value ** apArg)23665 static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23666 {
23667 	jx9_hashmap *pMap;
23668 	/* Make sure we are dealing with a valid hashmap */
23669 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23670 		/* Missing/Invalid arguments, return FALSE */
23671 		jx9_result_bool(pCtx, 0);
23672 		return JX9_OK;
23673 	}
23674 	/* Point to the internal representation of the input hashmap */
23675 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23676 	if( pMap->nEntry > 1 ){
23677 		sxi32 iCmpFlags = 0;
23678 		if( nArg > 1 ){
23679 			/* Extract comparison flags */
23680 			iCmpFlags = jx9_value_to_int(apArg[1]);
23681 			if( iCmpFlags == 3 /* SORT_REGULAR */ ){
23682 				iCmpFlags = 0; /* Standard comparison */
23683 			}
23684 		}
23685 		/* Do the merge sort */
23686 		HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
23687 		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
23688 		HashmapSortRehash(pMap);
23689 	}
23690 	/* All done, return TRUE */
23691 	jx9_result_bool(pCtx, 1);
23692 	return JX9_OK;
23693 }
23694 /*
23695  * bool usort(array &$array, callable $cmp_function)
23696  *  Sort an array by values using a user-defined comparison function.
23697  * Parameters
23698  *  $array
23699  *   The input array.
23700  * $cmp_function
23701  *  The comparison function must return an integer less than, equal to, or greater
23702  *  than zero if the first argument is considered to be respectively less than, equal
23703  *  to, or greater than the second.
23704  *    int callback ( mixed $a, mixed $b )
23705  * Return
23706  *  TRUE on success or FALSE on failure.
23707  */
jx9_hashmap_usort(jx9_context * pCtx,int nArg,jx9_value ** apArg)23708 static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg)
23709 {
23710 	jx9_hashmap *pMap;
23711 	/* Make sure we are dealing with a valid hashmap */
23712 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
23713 		/* Missing/Invalid arguments, return FALSE */
23714 		jx9_result_bool(pCtx, 0);
23715 		return JX9_OK;
23716 	}
23717 	/* Point to the internal representation of the input hashmap */
23718 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23719 	if( pMap->nEntry > 1 ){
23720 		jx9_value *pCallback = 0;
23721 		ProcNodeCmp xCmp;
23722 		xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
23723 		if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){
23724 			/* Point to the desired callback */
23725 			pCallback = apArg[1];
23726 		}else{
23727 			/* Use the default comparison function */
23728 			xCmp = HashmapCmpCallback1;
23729 		}
23730 		/* Do the merge sort */
23731 		HashmapMergeSort(pMap, xCmp, pCallback);
23732 		/* Rehash [Do not maintain index association as requested by the JX9 specification] */
23733 		HashmapSortRehash(pMap);
23734 	}
23735 	/* All done, return TRUE */
23736 	jx9_result_bool(pCtx, 1);
23737 	return JX9_OK;
23738 }
23739 /*
23740  * int count(array $var [, int $mode = COUNT_NORMAL ])
23741  *   Count all elements in an array, or something in an object.
23742  * Parameters
23743  *  $var
23744  *   The array or the object.
23745  * $mode
23746  *  If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
23747  *  will recursively count the array. This is particularly useful for counting
23748  *  all the elements of a multidimensional array. count() does not detect infinite
23749  *  recursion.
23750  * Return
23751  *  Returns the number of elements in the array.
23752  */
jx9_hashmap_count(jx9_context * pCtx,int nArg,jx9_value ** apArg)23753 static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
23754 {
23755 	int bRecursive = FALSE;
23756 	sxi64 iCount;
23757 	if( nArg < 1 ){
23758 		/* Missing arguments, return 0 */
23759 		jx9_result_int(pCtx, 0);
23760 		return JX9_OK;
23761 	}
23762 	if( !jx9_value_is_json_array(apArg[0]) ){
23763 		/* TICKET 1433-19: Handle objects */
23764 		int res = !jx9_value_is_null(apArg[0]);
23765 		jx9_result_int(pCtx, res);
23766 		return JX9_OK;
23767 	}
23768 	if( nArg > 1 ){
23769 		/* Recursive count? */
23770 		bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
23771 	}
23772 	/* Count */
23773 	iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
23774 	jx9_result_int64(pCtx, iCount);
23775 	return JX9_OK;
23776 }
23777 /*
23778  * bool array_key_exists(value $key, array $search)
23779  *  Checks if the given key or index exists in the array.
23780  * Parameters
23781  * $key
23782  *   Value to check.
23783  * $search
23784  *  An array with keys to check.
23785  * Return
23786  *  TRUE on success or FALSE on failure.
23787  */
jx9_hashmap_key_exists(jx9_context * pCtx,int nArg,jx9_value ** apArg)23788 static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
23789 {
23790 	sxi32 rc;
23791 	if( nArg < 2 ){
23792 		/* Missing arguments, return FALSE */
23793 		jx9_result_bool(pCtx, 0);
23794 		return JX9_OK;
23795 	}
23796 	/* Make sure we are dealing with a valid hashmap */
23797 	if( !jx9_value_is_json_array(apArg[1]) ){
23798 		/* Invalid argument, return FALSE */
23799 		jx9_result_bool(pCtx, 0);
23800 		return JX9_OK;
23801 	}
23802 	/* Perform the lookup */
23803 	rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
23804 	/* lookup result */
23805 	jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
23806 	return JX9_OK;
23807 }
23808 /*
23809  * value array_pop(array $array)
23810  *   POP the last inserted element from the array.
23811  * Parameter
23812  *  The array to get the value from.
23813  * Return
23814  *  Poped value or NULL on failure.
23815  */
jx9_hashmap_pop(jx9_context * pCtx,int nArg,jx9_value ** apArg)23816 static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg)
23817 {
23818 	jx9_hashmap *pMap;
23819 	if( nArg < 1 ){
23820 		/* Missing arguments, return null */
23821 		jx9_result_null(pCtx);
23822 		return JX9_OK;
23823 	}
23824 	/* Make sure we are dealing with a valid hashmap */
23825 	if( !jx9_value_is_json_array(apArg[0]) ){
23826 		/* Invalid argument, return null */
23827 		jx9_result_null(pCtx);
23828 		return JX9_OK;
23829 	}
23830 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23831 	if( pMap->nEntry < 1 ){
23832 		/* Noting to pop, return NULL */
23833 		jx9_result_null(pCtx);
23834 	}else{
23835 		jx9_hashmap_node *pLast = pMap->pLast;
23836 		jx9_value *pObj;
23837 		pObj = HashmapExtractNodeValue(pLast);
23838 		if( pObj ){
23839 			/* Node value */
23840 			jx9_result_value(pCtx, pObj);
23841 			/* Unlink the node */
23842 			jx9HashmapUnlinkNode(pLast);
23843 		}else{
23844 			jx9_result_null(pCtx);
23845 		}
23846 		/* Reset the cursor */
23847 		pMap->pCur = pMap->pFirst;
23848 	}
23849 	return JX9_OK;
23850 }
23851 /*
23852  * int array_push($array, $var, ...)
23853  *   Push one or more elements onto the end of array. (Stack insertion)
23854  * Parameters
23855  *  array
23856  *    The input array.
23857  *  var
23858  *   On or more value to push.
23859  * Return
23860  *  New array count (including old items).
23861  */
jx9_hashmap_push(jx9_context * pCtx,int nArg,jx9_value ** apArg)23862 static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg)
23863 {
23864 	jx9_hashmap *pMap;
23865 	sxi32 rc;
23866 	int i;
23867 	if( nArg < 1 ){
23868 		/* Missing arguments, return 0 */
23869 		jx9_result_int(pCtx, 0);
23870 		return JX9_OK;
23871 	}
23872 	/* Make sure we are dealing with a valid hashmap */
23873 	if( !jx9_value_is_json_array(apArg[0]) ){
23874 		/* Invalid argument, return 0 */
23875 		jx9_result_int(pCtx, 0);
23876 		return JX9_OK;
23877 	}
23878 	/* Point to the internal representation of the input hashmap */
23879 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23880 	/* Start pushing given values */
23881 	for( i = 1 ; i < nArg ; ++i ){
23882 		rc = jx9HashmapInsert(pMap, 0, apArg[i]);
23883 		if( rc != SXRET_OK ){
23884 			break;
23885 		}
23886 	}
23887 	/* Return the new count */
23888 	jx9_result_int64(pCtx, (sxi64)pMap->nEntry);
23889 	return JX9_OK;
23890 }
23891 /*
23892  * value array_shift(array $array)
23893  *   Shift an element off the beginning of array.
23894  * Parameter
23895  *  The array to get the value from.
23896  * Return
23897  *  Shifted value or NULL on failure.
23898  */
jx9_hashmap_shift(jx9_context * pCtx,int nArg,jx9_value ** apArg)23899 static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg)
23900 {
23901 	jx9_hashmap *pMap;
23902 	if( nArg < 1 ){
23903 		/* Missing arguments, return null */
23904 		jx9_result_null(pCtx);
23905 		return JX9_OK;
23906 	}
23907 	/* Make sure we are dealing with a valid hashmap */
23908 	if( !jx9_value_is_json_array(apArg[0]) ){
23909 		/* Invalid argument, return null */
23910 		jx9_result_null(pCtx);
23911 		return JX9_OK;
23912 	}
23913 	/* Point to the internal representation of the hashmap */
23914 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
23915 	if( pMap->nEntry < 1 ){
23916 		/* Empty hashmap, return NULL */
23917 		jx9_result_null(pCtx);
23918 	}else{
23919 		jx9_hashmap_node *pEntry = pMap->pFirst;
23920 		jx9_value *pObj;
23921 		sxu32 n;
23922 		pObj = HashmapExtractNodeValue(pEntry);
23923 		if( pObj ){
23924 			/* Node value */
23925 			jx9_result_value(pCtx, pObj);
23926 			/* Unlink the first node */
23927 			jx9HashmapUnlinkNode(pEntry);
23928 		}else{
23929 			jx9_result_null(pCtx);
23930 		}
23931 		/* Rehash all int keys */
23932 		n = pMap->nEntry;
23933 		pEntry = pMap->pFirst;
23934 		pMap->iNextIdx = 0; /* Reset the automatic index */
23935 		for(;;){
23936 			if( n < 1 ){
23937 				break;
23938 			}
23939 			if( pEntry->iType == HASHMAP_INT_NODE ){
23940 				HashmapRehashIntNode(pEntry);
23941 			}
23942 			/* Point to the next entry */
23943 			pEntry = pEntry->pPrev; /* Reverse link */
23944 			n--;
23945 		}
23946 		/* Reset the cursor */
23947 		pMap->pCur = pMap->pFirst;
23948 	}
23949 	return JX9_OK;
23950 }
23951 /*
23952  * Extract the node cursor value.
23953  */
HashmapCurrentValue(jx9_context * pCtx,jx9_hashmap * pMap,int iDirection)23954 static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection)
23955 {
23956 	jx9_hashmap_node *pCur = pMap->pCur;
23957 	jx9_value *pVal;
23958 	if( pCur == 0 ){
23959 		/* Cursor does not point to anything, return FALSE */
23960 		jx9_result_bool(pCtx, 0);
23961 		return JX9_OK;
23962 	}
23963 	if( iDirection != 0 ){
23964 		if( iDirection > 0 ){
23965 			/* Point to the next entry */
23966 			pMap->pCur = pCur->pPrev; /* Reverse link */
23967 			pCur = pMap->pCur;
23968 		}else{
23969 			/* Point to the previous entry */
23970 			pMap->pCur = pCur->pNext; /* Reverse link */
23971 			pCur = pMap->pCur;
23972 		}
23973 		if( pCur == 0 ){
23974 			/* End of input reached, return FALSE */
23975 			jx9_result_bool(pCtx, 0);
23976 			return JX9_OK;
23977 		}
23978 	}
23979 	/* Point to the desired element */
23980 	pVal = HashmapExtractNodeValue(pCur);
23981 	if( pVal ){
23982 		jx9_result_value(pCtx, pVal);
23983 	}else{
23984 		jx9_result_bool(pCtx, 0);
23985 	}
23986 	return JX9_OK;
23987 }
23988 /*
23989  * value current(array $array)
23990  *  Return the current element in an array.
23991  * Parameter
23992  *  $input: The input array.
23993  * Return
23994  *  The current() function simply returns the value of the array element that's currently
23995  *  being pointed to by the internal pointer. It does not move the pointer in any way.
23996  *  If the internal pointer points beyond the end of the elements list or the array
23997  *  is empty, current() returns FALSE.
23998  */
jx9_hashmap_current(jx9_context * pCtx,int nArg,jx9_value ** apArg)23999 static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg)
24000 {
24001 	if( nArg < 1 ){
24002 		/* Missing arguments, return FALSE */
24003 		jx9_result_bool(pCtx, 0);
24004 		return JX9_OK;
24005 	}
24006 	/* Make sure we are dealing with a valid hashmap */
24007 	if( !jx9_value_is_json_array(apArg[0]) ){
24008 		/* Invalid argument, return FALSE */
24009 		jx9_result_bool(pCtx, 0);
24010 		return JX9_OK;
24011 	}
24012 	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0);
24013 	return JX9_OK;
24014 }
24015 /*
24016  * value next(array $input)
24017  *  Advance the internal array pointer of an array.
24018  * Parameter
24019  *  $input: The input array.
24020  * Return
24021  *  next() behaves like current(), with one difference. It advances the internal array
24022  *  pointer one place forward before returning the element value. That means it returns
24023  *  the next array value and advances the internal array pointer by one.
24024  */
jx9_hashmap_next(jx9_context * pCtx,int nArg,jx9_value ** apArg)24025 static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg)
24026 {
24027 	if( nArg < 1 ){
24028 		/* Missing arguments, return FALSE */
24029 		jx9_result_bool(pCtx, 0);
24030 		return JX9_OK;
24031 	}
24032 	/* Make sure we are dealing with a valid hashmap */
24033 	if( !jx9_value_is_json_array(apArg[0]) ){
24034 		/* Invalid argument, return FALSE */
24035 		jx9_result_bool(pCtx, 0);
24036 		return JX9_OK;
24037 	}
24038 	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1);
24039 	return JX9_OK;
24040 }
24041 /*
24042  * value prev(array $input)
24043  *  Rewind the internal array pointer.
24044  * Parameter
24045  *  $input: The input array.
24046  * Return
24047  *  Returns the array value in the previous place that's pointed
24048  *  to by the internal array pointer, or FALSE if there are no more
24049  *  elements.
24050  */
jx9_hashmap_prev(jx9_context * pCtx,int nArg,jx9_value ** apArg)24051 static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg)
24052 {
24053 	if( nArg < 1 ){
24054 		/* Missing arguments, return FALSE */
24055 		jx9_result_bool(pCtx, 0);
24056 		return JX9_OK;
24057 	}
24058 	/* Make sure we are dealing with a valid hashmap */
24059 	if( !jx9_value_is_json_array(apArg[0]) ){
24060 		/* Invalid argument, return FALSE */
24061 		jx9_result_bool(pCtx, 0);
24062 		return JX9_OK;
24063 	}
24064 	HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1);
24065 	return JX9_OK;
24066 }
24067 /*
24068  * value end(array $input)
24069  *  Set the internal pointer of an array to its last element.
24070  * Parameter
24071  *  $input: The input array.
24072  * Return
24073  *  Returns the value of the last element or FALSE for empty array.
24074  */
jx9_hashmap_end(jx9_context * pCtx,int nArg,jx9_value ** apArg)24075 static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg)
24076 {
24077 	jx9_hashmap *pMap;
24078 	if( nArg < 1 ){
24079 		/* Missing arguments, return FALSE */
24080 		jx9_result_bool(pCtx, 0);
24081 		return JX9_OK;
24082 	}
24083 	/* Make sure we are dealing with a valid hashmap */
24084 	if( !jx9_value_is_json_array(apArg[0]) ){
24085 		/* Invalid argument, return FALSE */
24086 		jx9_result_bool(pCtx, 0);
24087 		return JX9_OK;
24088 	}
24089 	/* Point to the internal representation of the input hashmap */
24090 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24091 	/* Point to the last node */
24092 	pMap->pCur = pMap->pLast;
24093 	/* Return the last node value */
24094 	HashmapCurrentValue(&(*pCtx), pMap, 0);
24095 	return JX9_OK;
24096 }
24097 /*
24098  * value reset(array $array )
24099  *  Set the internal pointer of an array to its first element.
24100  * Parameter
24101  *  $input: The input array.
24102  * Return
24103  *  Returns the value of the first array element, or FALSE if the array is empty.
24104  */
jx9_hashmap_reset(jx9_context * pCtx,int nArg,jx9_value ** apArg)24105 static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg)
24106 {
24107 	jx9_hashmap *pMap;
24108 	if( nArg < 1 ){
24109 		/* Missing arguments, return FALSE */
24110 		jx9_result_bool(pCtx, 0);
24111 		return JX9_OK;
24112 	}
24113 	/* Make sure we are dealing with a valid hashmap */
24114 	if( !jx9_value_is_json_array(apArg[0]) ){
24115 		/* Invalid argument, return FALSE */
24116 		jx9_result_bool(pCtx, 0);
24117 		return JX9_OK;
24118 	}
24119 	/* Point to the internal representation of the input hashmap */
24120 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24121 	/* Point to the first node */
24122 	pMap->pCur = pMap->pFirst;
24123 	/* Return the last node value if available */
24124 	HashmapCurrentValue(&(*pCtx), pMap, 0);
24125 	return JX9_OK;
24126 }
24127 /*
24128  * value key(array $array)
24129  *   Fetch a key from an array
24130  * Parameter
24131  *  $input
24132  *   The input array.
24133  * Return
24134  *  The key() function simply returns the key of the array element that's currently
24135  *  being pointed to by the internal pointer. It does not move the pointer in any way.
24136  *  If the internal pointer points beyond the end of the elements list or the array
24137  *  is empty, key() returns NULL.
24138  */
jx9_hashmap_simple_key(jx9_context * pCtx,int nArg,jx9_value ** apArg)24139 static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg)
24140 {
24141 	jx9_hashmap_node *pCur;
24142 	jx9_hashmap *pMap;
24143 	if( nArg < 1 ){
24144 		/* Missing arguments, return NULL */
24145 		jx9_result_null(pCtx);
24146 		return JX9_OK;
24147 	}
24148 	/* Make sure we are dealing with a valid hashmap */
24149 	if( !jx9_value_is_json_array(apArg[0]) ){
24150 		/* Invalid argument, return NULL */
24151 		jx9_result_null(pCtx);
24152 		return JX9_OK;
24153 	}
24154 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24155 	pCur = pMap->pCur;
24156 	if( pCur == 0 ){
24157 		/* Cursor does not point to anything, return NULL */
24158 		jx9_result_null(pCtx);
24159 		return JX9_OK;
24160 	}
24161 	if( pCur->iType == HASHMAP_INT_NODE){
24162 		/* Key is integer */
24163 		jx9_result_int64(pCtx, pCur->xKey.iKey);
24164 	}else{
24165 		/* Key is blob */
24166 		jx9_result_string(pCtx,
24167 			(const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
24168 	}
24169 	return JX9_OK;
24170 }
24171 /*
24172  * array each(array $input)
24173  *  Return the current key and value pair from an array and advance the array cursor.
24174  * Parameter
24175  *  $input
24176  *    The input array.
24177  * Return
24178  *  Returns the current key and value pair from the array array. This pair is returned
24179  *  in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key
24180  *  contain the key name of the array element, and 1 and value contain the data.
24181  *  If the internal pointer for the array points past the end of the array contents
24182  *  each() returns FALSE.
24183  */
jx9_hashmap_each(jx9_context * pCtx,int nArg,jx9_value ** apArg)24184 static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg)
24185 {
24186 	jx9_hashmap_node *pCur;
24187 	jx9_hashmap *pMap;
24188 	jx9_value *pArray;
24189 	jx9_value *pVal;
24190 	jx9_value sKey;
24191 	if( nArg < 1 ){
24192 		/* Missing arguments, return FALSE */
24193 		jx9_result_bool(pCtx, 0);
24194 		return JX9_OK;
24195 	}
24196 	/* Make sure we are dealing with a valid hashmap */
24197 	if( !jx9_value_is_json_array(apArg[0]) ){
24198 		/* Invalid argument, return FALSE */
24199 		jx9_result_bool(pCtx, 0);
24200 		return JX9_OK;
24201 	}
24202 	/* Point to the internal representation that describe the input hashmap */
24203 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24204 	if( pMap->pCur == 0 ){
24205 		/* Cursor does not point to anything, return FALSE */
24206 		jx9_result_bool(pCtx, 0);
24207 		return JX9_OK;
24208 	}
24209 	pCur = pMap->pCur;
24210 	/* Create a new array */
24211 	pArray = jx9_context_new_array(pCtx);
24212 	if( pArray == 0 ){
24213 		jx9_result_bool(pCtx, 0);
24214 		return JX9_OK;
24215 	}
24216 	pVal = HashmapExtractNodeValue(pCur);
24217 	/* Insert the current value */
24218 	jx9_array_add_strkey_elem(pArray, "1", pVal);
24219 	jx9_array_add_strkey_elem(pArray, "value", pVal);
24220 	/* Make the key */
24221 	if( pCur->iType == HASHMAP_INT_NODE ){
24222 		jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
24223 	}else{
24224 		jx9MemObjInitFromString(pMap->pVm, &sKey, 0);
24225 		jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
24226 	}
24227 	/* Insert the current key */
24228 	jx9_array_add_elem(pArray, 0, &sKey);
24229 	jx9_array_add_strkey_elem(pArray, "key", &sKey);
24230 	jx9MemObjRelease(&sKey);
24231 	/* Advance the cursor */
24232 	pMap->pCur = pCur->pPrev; /* Reverse link */
24233 	/* Return the current entry */
24234 	jx9_result_value(pCtx, pArray);
24235 	return JX9_OK;
24236 }
24237 /*
24238  * array array_values(array $input)
24239  *   Returns all the values from the input array and indexes numerically the array.
24240  * Parameters
24241  *   input: The input array.
24242  * Return
24243  *  An indexed array of values or NULL on failure.
24244  */
jx9_hashmap_values(jx9_context * pCtx,int nArg,jx9_value ** apArg)24245 static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg)
24246 {
24247 	jx9_hashmap_node *pNode;
24248 	jx9_hashmap *pMap;
24249 	jx9_value *pArray;
24250 	jx9_value *pObj;
24251 	sxu32 n;
24252 	if( nArg < 1 ){
24253 		/* Missing arguments, return NULL */
24254 		jx9_result_null(pCtx);
24255 		return JX9_OK;
24256 	}
24257 	/* Make sure we are dealing with a valid hashmap */
24258 	if( !jx9_value_is_json_array(apArg[0]) ){
24259 		/* Invalid argument, return NULL */
24260 		jx9_result_null(pCtx);
24261 		return JX9_OK;
24262 	}
24263 	/* Point to the internal representation that describe the input hashmap */
24264 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24265 	/* Create a new array */
24266 	pArray = jx9_context_new_array(pCtx);
24267 	if( pArray == 0 ){
24268 		jx9_result_null(pCtx);
24269 		return JX9_OK;
24270 	}
24271 	/* Perform the requested operation */
24272 	pNode = pMap->pFirst;
24273 	for( n = 0 ; n < pMap->nEntry ; ++n ){
24274 		pObj = HashmapExtractNodeValue(pNode);
24275 		if( pObj ){
24276 			/* perform the insertion */
24277 			jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
24278 		}
24279 		/* Point to the next entry */
24280 		pNode = pNode->pPrev; /* Reverse link */
24281 	}
24282 	/* return the new array */
24283 	jx9_result_value(pCtx, pArray);
24284 	return JX9_OK;
24285 }
24286 /*
24287  * bool array_same(array $arr1, array $arr2)
24288  *  Return TRUE if the given arrays are the same instance.
24289  *  This function is useful under JX9 since arrays and objects
24290  *  are passed by reference.
24291  * Parameters
24292  *  $arr1
24293  *   First array
24294  *  $arr2
24295  *   Second array
24296  * Return
24297  *  TRUE if the arrays are the same instance. FALSE otherwise.
24298  * Note
24299  *  This function is a symisc eXtension.
24300  */
jx9_hashmap_same(jx9_context * pCtx,int nArg,jx9_value ** apArg)24301 static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg)
24302 {
24303 	jx9_hashmap *p1, *p2;
24304 	int rc;
24305 	if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
24306 		/* Missing or invalid arguments, return FALSE*/
24307 		jx9_result_bool(pCtx, 0);
24308 		return JX9_OK;
24309 	}
24310 	/* Point to the hashmaps */
24311 	p1 = (jx9_hashmap *)apArg[0]->x.pOther;
24312 	p2 = (jx9_hashmap *)apArg[1]->x.pOther;
24313 	rc = (p1 == p2);
24314 	/* Same instance? */
24315 	jx9_result_bool(pCtx, rc);
24316 	return JX9_OK;
24317 }
24318 /*
24319  * array array_merge(array $array1, ...)
24320  *  Merge one or more arrays.
24321  * Parameters
24322  *  $array1
24323  *    Initial array to merge.
24324  *  ...
24325  *   More array to merge.
24326  * Return
24327  *  The resulting array.
24328  */
jx9_hashmap_merge(jx9_context * pCtx,int nArg,jx9_value ** apArg)24329 static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg)
24330 {
24331 	jx9_hashmap *pMap, *pSrc;
24332 	jx9_value *pArray;
24333 	int i;
24334 	if( nArg < 1 ){
24335 		/* Missing arguments, return NULL */
24336 		jx9_result_null(pCtx);
24337 		return JX9_OK;
24338 	}
24339 	/* Create a new array */
24340 	pArray = jx9_context_new_array(pCtx);
24341 	if( pArray == 0 ){
24342 		jx9_result_null(pCtx);
24343 		return JX9_OK;
24344 	}
24345 	/* Point to the internal representation of the hashmap */
24346 	pMap = (jx9_hashmap *)pArray->x.pOther;
24347 	/* Start merging */
24348 	for( i = 0 ; i < nArg ; i++ ){
24349 		/* Make sure we are dealing with a valid hashmap */
24350 		if( !jx9_value_is_json_array(apArg[i]) ){
24351 			/* Insert scalar value */
24352 			jx9_array_add_elem(pArray, 0, apArg[i]);
24353 		}else{
24354 			pSrc = (jx9_hashmap *)apArg[i]->x.pOther;
24355 			/* Merge the two hashmaps */
24356 			HashmapMerge(pSrc, pMap);
24357 		}
24358 	}
24359 	/* Return the freshly created array */
24360 	jx9_result_value(pCtx, pArray);
24361 	return JX9_OK;
24362 }
24363 /*
24364  * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ])
24365  *  Checks if a value exists in an array.
24366  * Parameters
24367  *  $needle
24368  *   The searched value.
24369  *   Note:
24370  *    If needle is a string, the comparison is done in a case-sensitive manner.
24371  * $haystack
24372  *  The target array.
24373  * $strict
24374  *  If the third parameter strict is set to TRUE then the in_array() function
24375  *  will also check the types of the needle in the haystack.
24376  */
jx9_hashmap_in_array(jx9_context * pCtx,int nArg,jx9_value ** apArg)24377 static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
24378 {
24379 	jx9_value *pNeedle;
24380 	int bStrict;
24381 	int rc;
24382 	if( nArg < 2 ){
24383 		/* Missing argument, return FALSE */
24384 		jx9_result_bool(pCtx, 0);
24385 		return JX9_OK;
24386 	}
24387 	pNeedle = apArg[0];
24388 	bStrict = 0;
24389 	if( nArg > 2 ){
24390 		bStrict = jx9_value_to_bool(apArg[2]);
24391 	}
24392 	if( !jx9_value_is_json_array(apArg[1]) ){
24393 		/* haystack must be an array, perform a standard comparison */
24394 		rc = jx9_value_compare(pNeedle, apArg[1], bStrict);
24395 		/* Set the comparison result */
24396 		jx9_result_bool(pCtx, rc == 0);
24397 		return JX9_OK;
24398 	}
24399 	/* Perform the lookup */
24400 	rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
24401 	/* Lookup result */
24402 	jx9_result_bool(pCtx, rc == SXRET_OK);
24403 	return JX9_OK;
24404 }
24405 /*
24406  * array array_copy(array $source)
24407  *  Make a blind copy of the target array.
24408  * Parameters
24409  *  $source
24410  *   Target array
24411  * Return
24412  *  Copy of the target array on success. NULL otherwise.
24413  * Note
24414  *  This function is a symisc eXtension.
24415  */
jx9_hashmap_copy(jx9_context * pCtx,int nArg,jx9_value ** apArg)24416 static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
24417 {
24418 	jx9_hashmap *pMap;
24419 	jx9_value *pArray;
24420 	if( nArg < 1 ){
24421 		/* Missing arguments, return NULL */
24422 		jx9_result_null(pCtx);
24423 		return JX9_OK;
24424 	}
24425 	/* Create a new array */
24426 	pArray = jx9_context_new_array(pCtx);
24427 	if( pArray == 0 ){
24428 		jx9_result_null(pCtx);
24429 		return JX9_OK;
24430 	}
24431 	/* Point to the internal representation of the hashmap */
24432 	pMap = (jx9_hashmap *)pArray->x.pOther;
24433 	if( jx9_value_is_json_array(apArg[0])){
24434 		/* Point to the internal representation of the source */
24435 		jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24436 		/* Perform the copy */
24437 		jx9HashmapDup(pSrc, pMap);
24438 	}else{
24439 		/* Simple insertion */
24440 		jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]);
24441 	}
24442 	/* Return the duplicated array */
24443 	jx9_result_value(pCtx, pArray);
24444 	return JX9_OK;
24445 }
24446 /*
24447  * bool array_erase(array $source)
24448  *  Remove all elements from a given array.
24449  * Parameters
24450  *  $source
24451  *   Target array
24452  * Return
24453  *  TRUE on success. FALSE otherwise.
24454  * Note
24455  *  This function is a symisc eXtension.
24456  */
jx9_hashmap_erase(jx9_context * pCtx,int nArg,jx9_value ** apArg)24457 static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg)
24458 {
24459 	jx9_hashmap *pMap;
24460 	if( nArg < 1 ){
24461 		/* Missing arguments */
24462 		jx9_result_bool(pCtx, 0);
24463 		return JX9_OK;
24464 	}
24465 	/* Point to the target hashmap */
24466 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24467 	/* Erase */
24468 	jx9HashmapRelease(pMap, FALSE);
24469 	return JX9_OK;
24470 }
24471 /*
24472  * array array_diff(array $array1, array $array2, ...)
24473  *  Computes the difference of arrays.
24474  * Parameters
24475  *  $array1
24476  *    The array to compare from
24477  *  $array2
24478  *    An array to compare against
24479  *  $...
24480  *   More arrays to compare against
24481  * Return
24482  *  Returns an array containing all the entries from array1 that
24483  *  are not present in any of the other arrays.
24484  */
jx9_hashmap_diff(jx9_context * pCtx,int nArg,jx9_value ** apArg)24485 static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg)
24486 {
24487 	jx9_hashmap_node *pEntry;
24488 	jx9_hashmap *pSrc, *pMap;
24489 	jx9_value *pArray;
24490 	jx9_value *pVal;
24491 	sxi32 rc;
24492 	sxu32 n;
24493 	int i;
24494 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
24495 		/* Missing arguments, return NULL */
24496 		jx9_result_null(pCtx);
24497 		return JX9_OK;
24498 	}
24499 	if( nArg == 1 ){
24500 		/* Return the first array since we cannot perform a diff */
24501 		jx9_result_value(pCtx, apArg[0]);
24502 		return JX9_OK;
24503 	}
24504 	/* Create a new array */
24505 	pArray = jx9_context_new_array(pCtx);
24506 	if( pArray == 0 ){
24507 		jx9_result_null(pCtx);
24508 		return JX9_OK;
24509 	}
24510 	/* Point to the internal representation of the source hashmap */
24511 	pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24512 	/* Perform the diff */
24513 	pEntry = pSrc->pFirst;
24514 	n = pSrc->nEntry;
24515 	for(;;){
24516 		if( n < 1 ){
24517 			break;
24518 		}
24519 		/* Extract the node value */
24520 		pVal = HashmapExtractNodeValue(pEntry);
24521 		if( pVal ){
24522 			for( i = 1 ; i < nArg ; i++ ){
24523 				if( !jx9_value_is_json_array(apArg[i])) {
24524 					/* ignore */
24525 					continue;
24526 				}
24527 				/* Point to the internal representation of the hashmap */
24528 				pMap = (jx9_hashmap *)apArg[i]->x.pOther;
24529 				/* Perform the lookup */
24530 				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
24531 				if( rc == SXRET_OK ){
24532 					/* Value exist */
24533 					break;
24534 				}
24535 			}
24536 			if( i >= nArg ){
24537 				/* Perform the insertion */
24538 				HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
24539 			}
24540 		}
24541 		/* Point to the next entry */
24542 		pEntry = pEntry->pPrev; /* Reverse link */
24543 		n--;
24544 	}
24545 	/* Return the freshly created array */
24546 	jx9_result_value(pCtx, pArray);
24547 	return JX9_OK;
24548 }
24549 /*
24550  * array array_intersect(array $array1 , array $array2, ...)
24551  *  Computes the intersection of arrays.
24552  * Parameters
24553  *  $array1
24554  *    The array to compare from
24555  *  $array2
24556  *    An array to compare against
24557  *  $...
24558  *   More arrays to compare against
24559  * Return
24560  *  Returns an array containing all of the values in array1 whose values exist
24561  *  in all of the parameters. .
24562  * Note that NULL is returned on failure.
24563  */
jx9_hashmap_intersect(jx9_context * pCtx,int nArg,jx9_value ** apArg)24564 static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg)
24565 {
24566 	jx9_hashmap_node *pEntry;
24567 	jx9_hashmap *pSrc, *pMap;
24568 	jx9_value *pArray;
24569 	jx9_value *pVal;
24570 	sxi32 rc;
24571 	sxu32 n;
24572 	int i;
24573 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
24574 		/* Missing arguments, return NULL */
24575 		jx9_result_null(pCtx);
24576 		return JX9_OK;
24577 	}
24578 	if( nArg == 1 ){
24579 		/* Return the first array since we cannot perform a diff */
24580 		jx9_result_value(pCtx, apArg[0]);
24581 		return JX9_OK;
24582 	}
24583 	/* Create a new array */
24584 	pArray = jx9_context_new_array(pCtx);
24585 	if( pArray == 0 ){
24586 		jx9_result_null(pCtx);
24587 		return JX9_OK;
24588 	}
24589 	/* Point to the internal representation of the source hashmap */
24590 	pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
24591 	/* Perform the intersection */
24592 	pEntry = pSrc->pFirst;
24593 	n = pSrc->nEntry;
24594 	for(;;){
24595 		if( n < 1 ){
24596 			break;
24597 		}
24598 		/* Extract the node value */
24599 		pVal = HashmapExtractNodeValue(pEntry);
24600 		if( pVal ){
24601 			for( i = 1 ; i < nArg ; i++ ){
24602 				if( !jx9_value_is_json_array(apArg[i])) {
24603 					/* ignore */
24604 					continue;
24605 				}
24606 				/* Point to the internal representation of the hashmap */
24607 				pMap = (jx9_hashmap *)apArg[i]->x.pOther;
24608 				/* Perform the lookup */
24609 				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
24610 				if( rc != SXRET_OK ){
24611 					/* Value does not exist */
24612 					break;
24613 				}
24614 			}
24615 			if( i >= nArg ){
24616 				/* Perform the insertion */
24617 				HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
24618 			}
24619 		}
24620 		/* Point to the next entry */
24621 		pEntry = pEntry->pPrev; /* Reverse link */
24622 		n--;
24623 	}
24624 	/* Return the freshly created array */
24625 	jx9_result_value(pCtx, pArray);
24626 	return JX9_OK;
24627 }
24628 /*
24629  * number array_sum(array $array )
24630  *  Calculate the sum of values in an array.
24631  * Parameters
24632  *  $array: The input array.
24633  * Return
24634  *  Returns the sum of values as an integer or float.
24635  */
DoubleSum(jx9_context * pCtx,jx9_hashmap * pMap)24636 static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap)
24637 {
24638 	jx9_hashmap_node *pEntry;
24639 	jx9_value *pObj;
24640 	double dSum = 0;
24641 	sxu32 n;
24642 	pEntry = pMap->pFirst;
24643 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24644 		pObj = HashmapExtractNodeValue(pEntry);
24645 		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24646 			if( pObj->iFlags & MEMOBJ_REAL ){
24647 				dSum += pObj->x.rVal;
24648 			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24649 				dSum += (double)pObj->x.iVal;
24650 			}else if( pObj->iFlags & MEMOBJ_STRING ){
24651 				if( SyBlobLength(&pObj->sBlob) > 0 ){
24652 					double dv = 0;
24653 					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
24654 					dSum += dv;
24655 				}
24656 			}
24657 		}
24658 		/* Point to the next entry */
24659 		pEntry = pEntry->pPrev; /* Reverse link */
24660 	}
24661 	/* Return sum */
24662 	jx9_result_double(pCtx, dSum);
24663 }
Int64Sum(jx9_context * pCtx,jx9_hashmap * pMap)24664 static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap)
24665 {
24666 	jx9_hashmap_node *pEntry;
24667 	jx9_value *pObj;
24668 	sxi64 nSum = 0;
24669 	sxu32 n;
24670 	pEntry = pMap->pFirst;
24671 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24672 		pObj = HashmapExtractNodeValue(pEntry);
24673 		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24674 			if( pObj->iFlags & MEMOBJ_REAL ){
24675 				nSum += (sxi64)pObj->x.rVal;
24676 			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24677 				nSum += pObj->x.iVal;
24678 			}else if( pObj->iFlags & MEMOBJ_STRING ){
24679 				if( SyBlobLength(&pObj->sBlob) > 0 ){
24680 					sxi64 nv = 0;
24681 					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
24682 					nSum += nv;
24683 				}
24684 			}
24685 		}
24686 		/* Point to the next entry */
24687 		pEntry = pEntry->pPrev; /* Reverse link */
24688 	}
24689 	/* Return sum */
24690 	jx9_result_int64(pCtx, nSum);
24691 }
24692 /* number array_sum(array $array )
24693  * (See block-coment above)
24694  */
jx9_hashmap_sum(jx9_context * pCtx,int nArg,jx9_value ** apArg)24695 static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg)
24696 {
24697 	jx9_hashmap *pMap;
24698 	jx9_value *pObj;
24699 	if( nArg < 1 ){
24700 		/* Missing arguments, return 0 */
24701 		jx9_result_int(pCtx, 0);
24702 		return JX9_OK;
24703 	}
24704 	/* Make sure we are dealing with a valid hashmap */
24705 	if( !jx9_value_is_json_array(apArg[0]) ){
24706 		/* Invalid argument, return 0 */
24707 		jx9_result_int(pCtx, 0);
24708 		return JX9_OK;
24709 	}
24710 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24711 	if( pMap->nEntry < 1 ){
24712 		/* Nothing to compute, return 0 */
24713 		jx9_result_int(pCtx, 0);
24714 		return JX9_OK;
24715 	}
24716 	/* If the first element is of type float, then perform floating
24717 	 * point computaion.Otherwise switch to int64 computaion.
24718 	 */
24719 	pObj = HashmapExtractNodeValue(pMap->pFirst);
24720 	if( pObj == 0 ){
24721 		jx9_result_int(pCtx, 0);
24722 		return JX9_OK;
24723 	}
24724 	if( pObj->iFlags & MEMOBJ_REAL ){
24725 		DoubleSum(pCtx, pMap);
24726 	}else{
24727 		Int64Sum(pCtx, pMap);
24728 	}
24729 	return JX9_OK;
24730 }
24731 /*
24732  * number array_product(array $array )
24733  *  Calculate the product of values in an array.
24734  * Parameters
24735  *  $array: The input array.
24736  * Return
24737  *  Returns the product of values as an integer or float.
24738  */
DoubleProd(jx9_context * pCtx,jx9_hashmap * pMap)24739 static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap)
24740 {
24741 	jx9_hashmap_node *pEntry;
24742 	jx9_value *pObj;
24743 	double dProd;
24744 	sxu32 n;
24745 	pEntry = pMap->pFirst;
24746 	dProd = 1;
24747 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24748 		pObj = HashmapExtractNodeValue(pEntry);
24749 		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24750 			if( pObj->iFlags & MEMOBJ_REAL ){
24751 				dProd *= pObj->x.rVal;
24752 			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24753 				dProd *= (double)pObj->x.iVal;
24754 			}else if( pObj->iFlags & MEMOBJ_STRING ){
24755 				if( SyBlobLength(&pObj->sBlob) > 0 ){
24756 					double dv = 0;
24757 					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
24758 					dProd *= dv;
24759 				}
24760 			}
24761 		}
24762 		/* Point to the next entry */
24763 		pEntry = pEntry->pPrev; /* Reverse link */
24764 	}
24765 	/* Return product */
24766 	jx9_result_double(pCtx, dProd);
24767 }
Int64Prod(jx9_context * pCtx,jx9_hashmap * pMap)24768 static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap)
24769 {
24770 	jx9_hashmap_node *pEntry;
24771 	jx9_value *pObj;
24772 	sxi64 nProd;
24773 	sxu32 n;
24774 	pEntry = pMap->pFirst;
24775 	nProd = 1;
24776 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24777 		pObj = HashmapExtractNodeValue(pEntry);
24778 		if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
24779 			if( pObj->iFlags & MEMOBJ_REAL ){
24780 				nProd *= (sxi64)pObj->x.rVal;
24781 			}else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
24782 				nProd *= pObj->x.iVal;
24783 			}else if( pObj->iFlags & MEMOBJ_STRING ){
24784 				if( SyBlobLength(&pObj->sBlob) > 0 ){
24785 					sxi64 nv = 0;
24786 					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
24787 					nProd *= nv;
24788 				}
24789 			}
24790 		}
24791 		/* Point to the next entry */
24792 		pEntry = pEntry->pPrev; /* Reverse link */
24793 	}
24794 	/* Return product */
24795 	jx9_result_int64(pCtx, nProd);
24796 }
24797 /* number array_product(array $array )
24798  * (See block-block comment above)
24799  */
jx9_hashmap_product(jx9_context * pCtx,int nArg,jx9_value ** apArg)24800 static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg)
24801 {
24802 	jx9_hashmap *pMap;
24803 	jx9_value *pObj;
24804 	if( nArg < 1 ){
24805 		/* Missing arguments, return 0 */
24806 		jx9_result_int(pCtx, 0);
24807 		return JX9_OK;
24808 	}
24809 	/* Make sure we are dealing with a valid hashmap */
24810 	if( !jx9_value_is_json_array(apArg[0]) ){
24811 		/* Invalid argument, return 0 */
24812 		jx9_result_int(pCtx, 0);
24813 		return JX9_OK;
24814 	}
24815 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24816 	if( pMap->nEntry < 1 ){
24817 		/* Nothing to compute, return 0 */
24818 		jx9_result_int(pCtx, 0);
24819 		return JX9_OK;
24820 	}
24821 	/* If the first element is of type float, then perform floating
24822 	 * point computaion.Otherwise switch to int64 computaion.
24823 	 */
24824 	pObj = HashmapExtractNodeValue(pMap->pFirst);
24825 	if( pObj == 0 ){
24826 		jx9_result_int(pCtx, 0);
24827 		return JX9_OK;
24828 	}
24829 	if( pObj->iFlags & MEMOBJ_REAL ){
24830 		DoubleProd(pCtx, pMap);
24831 	}else{
24832 		Int64Prod(pCtx, pMap);
24833 	}
24834 	return JX9_OK;
24835 }
24836 /*
24837  * array array_map(callback $callback, array $arr1)
24838  *  Applies the callback to the elements of the given arrays.
24839  * Parameters
24840  *  $callback
24841  *   Callback function to run for each element in each array.
24842  * $arr1
24843  *   An array to run through the callback function.
24844  * Return
24845  *  Returns an array containing all the elements of arr1 after applying
24846  *  the callback function to each one.
24847  * NOTE:
24848  *  array_map() passes only a single value to the callback.
24849  */
jx9_hashmap_map(jx9_context * pCtx,int nArg,jx9_value ** apArg)24850 static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg)
24851 {
24852 	jx9_value *pArray, *pValue, sKey, sResult;
24853 	jx9_hashmap_node *pEntry;
24854 	jx9_hashmap *pMap;
24855 	sxu32 n;
24856 	if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){
24857 		/* Invalid arguments, return NULL */
24858 		jx9_result_null(pCtx);
24859 		return JX9_OK;
24860 	}
24861 	/* Create a new array */
24862 	pArray = jx9_context_new_array(pCtx);
24863 	if( pArray == 0 ){
24864 		jx9_result_null(pCtx);
24865 		return JX9_OK;
24866 	}
24867 	/* Point to the internal representation of the input hashmap */
24868 	pMap = (jx9_hashmap *)apArg[1]->x.pOther;
24869 	jx9MemObjInit(pMap->pVm, &sResult);
24870 	jx9MemObjInit(pMap->pVm, &sKey);
24871 	/* Perform the requested operation */
24872 	pEntry = pMap->pFirst;
24873 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24874 		/* Extrcat the node value */
24875 		pValue = HashmapExtractNodeValue(pEntry);
24876 		if( pValue ){
24877 			sxi32 rc;
24878 			/* Invoke the supplied callback */
24879 			rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
24880 			/* Extract the node key */
24881 			jx9HashmapExtractNodeKey(pEntry, &sKey);
24882 			if( rc != SXRET_OK ){
24883 				/* An error occured while invoking the supplied callback [i.e: not defined] */
24884 				jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
24885 			}else{
24886 				/* Insert the callback return value */
24887 				jx9_array_add_elem(pArray, &sKey, &sResult);
24888 			}
24889 			jx9MemObjRelease(&sKey);
24890 			jx9MemObjRelease(&sResult);
24891 		}
24892 		/* Point to the next entry */
24893 		pEntry = pEntry->pPrev; /* Reverse link */
24894 	}
24895 	jx9_result_value(pCtx, pArray);
24896 	return JX9_OK;
24897 }
24898 /*
24899  * bool array_walk(array &$array, callback $funcname [, value $userdata ] )
24900  *  Apply a user function to every member of an array.
24901  * Parameters
24902  *  $array
24903  *   The input array.
24904  * $funcname
24905  *  Typically, funcname takes on two parameters.The array parameter's value being
24906  *  the first, and the key/index second.
24907  * Note:
24908  *  If funcname needs to be working with the actual values of the array, specify the first
24909  *  parameter of funcname as a reference. Then, any changes made to those elements will
24910  *  be made in the original array itself.
24911  * $userdata
24912  *  If the optional userdata parameter is supplied, it will be passed as the third parameter
24913  *  to the callback funcname.
24914  * Return
24915  *  Returns TRUE on success or FALSE on failure.
24916  */
jx9_hashmap_walk(jx9_context * pCtx,int nArg,jx9_value ** apArg)24917 static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg)
24918 {
24919 	jx9_value *pValue, *pUserData, sKey;
24920 	jx9_hashmap_node *pEntry;
24921 	jx9_hashmap *pMap;
24922 	sxi32 rc;
24923 	sxu32 n;
24924 	if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){
24925 		/* Invalid/Missing arguments, return FALSE */
24926 		jx9_result_bool(pCtx, 0);
24927 		return JX9_OK;
24928 	}
24929 	pUserData = nArg > 2 ? apArg[2] : 0;
24930 	/* Point to the internal representation of the input hashmap */
24931 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
24932 	jx9MemObjInit(pMap->pVm, &sKey);
24933 	/* Perform the desired operation */
24934 	pEntry = pMap->pFirst;
24935 	for( n = 0 ; n < pMap->nEntry ; n++ ){
24936 		/* Extract the node value */
24937 		pValue = HashmapExtractNodeValue(pEntry);
24938 		if( pValue ){
24939 			/* Extract the entry key */
24940 			jx9HashmapExtractNodeKey(pEntry, &sKey);
24941 			/* Invoke the supplied callback */
24942 			rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
24943 			jx9MemObjRelease(&sKey);
24944 			if( rc != SXRET_OK ){
24945 				/* An error occured while invoking the supplied callback [i.e: not defined] */
24946 				jx9_result_bool(pCtx, 0); /* return FALSE */
24947 				return JX9_OK;
24948 			}
24949 		}
24950 		/* Point to the next entry */
24951 		pEntry = pEntry->pPrev; /* Reverse link */
24952 	}
24953 	/* All done, return TRUE */
24954 	jx9_result_bool(pCtx, 1);
24955 	return JX9_OK;
24956 }
24957 /*
24958  * Table of built-in hashmap functions.
24959  */
24960 static const jx9_builtin_func aHashmapFunc[] = {
24961 	{"count",             jx9_hashmap_count },
24962 	{"sizeof",            jx9_hashmap_count },
24963 	{"array_key_exists",  jx9_hashmap_key_exists },
24964 	{"array_pop",         jx9_hashmap_pop     },
24965 	{"array_push",        jx9_hashmap_push    },
24966 	{"array_shift",       jx9_hashmap_shift   },
24967 	{"array_product",     jx9_hashmap_product },
24968 	{"array_sum",         jx9_hashmap_sum     },
24969 	{"array_values",      jx9_hashmap_values  },
24970 	{"array_same",        jx9_hashmap_same    },
24971 	{"array_merge",       jx9_hashmap_merge   },
24972 	{"array_diff",        jx9_hashmap_diff    },
24973 	{"array_intersect",   jx9_hashmap_intersect},
24974 	{"in_array",          jx9_hashmap_in_array },
24975 	{"array_copy",        jx9_hashmap_copy    },
24976 	{"array_erase",       jx9_hashmap_erase   },
24977 	{"array_map",         jx9_hashmap_map     },
24978 	{"array_walk",        jx9_hashmap_walk    },
24979 	{"sort",              jx9_hashmap_sort    },
24980 	{"rsort",             jx9_hashmap_rsort   },
24981 	{"usort",             jx9_hashmap_usort   },
24982 	{"current",           jx9_hashmap_current },
24983 	{"each",              jx9_hashmap_each    },
24984 	{"pos",               jx9_hashmap_current },
24985 	{"next",              jx9_hashmap_next    },
24986 	{"prev",              jx9_hashmap_prev    },
24987 	{"end",               jx9_hashmap_end     },
24988 	{"reset",             jx9_hashmap_reset   },
24989 	{"key",               jx9_hashmap_simple_key }
24990 };
24991 /*
24992  * Register the built-in hashmap functions defined above.
24993  */
jx9RegisterHashmapFunctions(jx9_vm * pVm)24994 JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm)
24995 {
24996 	sxu32 n;
24997 	for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){
24998 		jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
24999 	}
25000 }
25001 /*
25002  * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each
25003  * retrieved entry.
25004  * Note that argument are passed to the callback by copy. That is, any modification to
25005  * the entry value in the callback body will not alter the real value.
25006  * If the callback wishes to abort processing [i.e: it's invocation] it must return
25007  * a value different from JX9_OK.
25008  * Refer to [jx9_array_walk()] for more information.
25009  */
jx9HashmapWalk(jx9_hashmap * pMap,int (* xWalk)(jx9_value *,jx9_value *,void *),void * pUserData)25010 JX9_PRIVATE sxi32 jx9HashmapWalk(
25011 	jx9_hashmap *pMap, /* Target hashmap */
25012 	int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */
25013 	void *pUserData /* Last argument to xWalk() */
25014 	)
25015 {
25016 	jx9_hashmap_node *pEntry;
25017 	jx9_value sKey, sValue;
25018 	sxi32 rc;
25019 	sxu32 n;
25020 	/* Initialize walker parameter */
25021 	rc = SXRET_OK;
25022 	jx9MemObjInit(pMap->pVm, &sKey);
25023 	jx9MemObjInit(pMap->pVm, &sValue);
25024 	n = pMap->nEntry;
25025 	pEntry = pMap->pFirst;
25026 	/* Start the iteration process */
25027 	for(;;){
25028 		if( n < 1 ){
25029 			break;
25030 		}
25031 		/* Extract a copy of the key and a copy the current value */
25032 		jx9HashmapExtractNodeKey(pEntry, &sKey);
25033 		jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE);
25034 		/* Invoke the user callback */
25035 		rc = xWalk(&sKey, &sValue, pUserData);
25036 		/* Release the copy of the key and the value */
25037 		jx9MemObjRelease(&sKey);
25038 		jx9MemObjRelease(&sValue);
25039 		if( rc != JX9_OK ){
25040 			/* Callback request an operation abort */
25041 			return SXERR_ABORT;
25042 		}
25043 		/* Point to the next entry */
25044 		pEntry = pEntry->pPrev; /* Reverse link */
25045 		n--;
25046 	}
25047 	/* All done */
25048 	return SXRET_OK;
25049 }
25050 
25051 /* jx9_json.c */
25052 /*
25053  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
25054  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
25055  * Version 1.7.2
25056  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
25057  * please contact Symisc Systems via:
25058  *       legal@symisc.net
25059  *       licensing@symisc.net
25060  *       contact@symisc.net
25061  * or visit:
25062  *      http://jx9.symisc.net/
25063  */
25064  /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
25065 #ifndef JX9_AMALGAMATION
25066 #include "jx9Int.h"
25067 #endif
25068 /* This file deals with JSON serialization, decoding and stuff like that. */
25069 /*
25070  * Section:
25071  *  JSON encoding/decoding routines.
25072  * Authors:
25073  *  Symisc Systems, devel@symisc.net.
25074  *  Copyright (C) Symisc Systems, http://jx9.symisc.net
25075  * Status:
25076  *    Devel.
25077  */
25078 /* Forward reference */
25079 static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
25080 static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
25081 /*
25082  * JSON encoder state is stored in an instance
25083  * of the following structure.
25084  */
25085 typedef struct json_private_data json_private_data;
25086 struct json_private_data
25087 {
25088 	SyBlob *pOut;      /* Output consumer buffer */
25089 	int isFirst;       /* True if first encoded entry */
25090 	int iFlags;        /* JSON encoding flags */
25091 	int nRecCount;     /* Recursion count */
25092 };
25093 /*
25094  * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
25095  * According to wikipedia
25096  * JSON's basic types are:
25097  *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
25098  *   String (double-quoted Unicode, with backslash escaping)
25099  *   Boolean (true or false)
25100  *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
25101  *    do not need to be of the same type)
25102  *   Object (an unordered collection of key:value pairs with the ':' character separating the key
25103  *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
25104  *     be distinct from each other)
25105  *   null (empty)
25106  * Non-significant white space may be added freely around the "structural characters"
25107  * (i.e. the brackets "[{]}", colon ":" and comma ",").
25108  */
VmJsonEncode(jx9_value * pIn,json_private_data * pData)25109 static sxi32 VmJsonEncode(
25110 	jx9_value *pIn,          /* Encode this value */
25111 	json_private_data *pData /* Context data */
25112 	){
25113 		SyBlob *pOut = pData->pOut;
25114 		int nByte;
25115 		if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
25116 			/* null */
25117 			SyBlobAppend(pOut, "null", sizeof("null")-1);
25118 		}else if( jx9_value_is_bool(pIn) ){
25119 			int iBool = jx9_value_to_bool(pIn);
25120 			sxu32 iLen;
25121 			/* true/false */
25122 			iLen = iBool ? sizeof("true") : sizeof("false");
25123 			SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
25124 		}else if(  jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
25125 			const char *zNum;
25126 			/* Get a string representation of the number */
25127 			zNum = jx9_value_to_string(pIn, &nByte);
25128 			SyBlobAppend(pOut,zNum,nByte);
25129 		}else if( jx9_value_is_string(pIn) ){
25130 				const char *zIn, *zEnd;
25131 				int c;
25132 				/* Encode the string */
25133 				zIn = jx9_value_to_string(pIn, &nByte);
25134 				zEnd = &zIn[nByte];
25135 				/* Append the double quote */
25136 				SyBlobAppend(pOut,"\"", sizeof(char));
25137 				for(;;){
25138 					if( zIn >= zEnd ){
25139 						/* No more input to process */
25140 						break;
25141 					}
25142 					c = zIn[0];
25143 					/* Advance the stream cursor */
25144 					zIn++;
25145 					if( c == '"' || c == '\\' ){
25146 						/* Unescape the character */
25147 						SyBlobAppend(pOut,"\\", sizeof(char));
25148 					}
25149 					/* Append character verbatim */
25150 					SyBlobAppend(pOut,(const char *)&c,sizeof(char));
25151 				}
25152 				/* Append the double quote */
25153 				SyBlobAppend(pOut,"\"",sizeof(char));
25154 		}else if( jx9_value_is_json_array(pIn) ){
25155 			/* Encode the array/object */
25156 			pData->isFirst = 1;
25157 			if( jx9_value_is_json_object(pIn) ){
25158 				/* Encode the object instance */
25159 				pData->isFirst = 1;
25160 				/* Append the curly braces */
25161 				SyBlobAppend(pOut, "{",sizeof(char));
25162 				/* Iterate throw object attribute */
25163 				jx9_array_walk(pIn,VmJsonObjectEncode, pData);
25164 				/* Append the closing curly braces  */
25165 				SyBlobAppend(pOut, "}",sizeof(char));
25166 			}else{
25167 				/* Append the square bracket or curly braces */
25168 				SyBlobAppend(pOut,"[",sizeof(char));
25169 				/* Iterate throw array entries */
25170 				jx9_array_walk(pIn, VmJsonArrayEncode, pData);
25171 				/* Append the closing square bracket or curly braces */
25172 				SyBlobAppend(pOut,"]",sizeof(char));
25173 			}
25174 		}else{
25175 			/* Can't happen */
25176 			SyBlobAppend(pOut,"null",sizeof("null")-1);
25177 		}
25178 		/* All done */
25179 		return JX9_OK;
25180 }
25181 /*
25182  * The following walker callback is invoked each time we need
25183  * to encode an array to JSON.
25184  */
VmJsonArrayEncode(jx9_value * pKey,jx9_value * pValue,void * pUserData)25185 static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
25186 {
25187 	json_private_data *pJson = (json_private_data *)pUserData;
25188 	if( pJson->nRecCount > 31 ){
25189 		/* Recursion limit reached, return immediately */
25190 		SXUNUSED(pKey); /* cc warning */
25191 		return JX9_OK;
25192 	}
25193 	if( !pJson->isFirst ){
25194 		/* Append the colon first */
25195 		SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
25196 	}
25197 	/* Encode the value */
25198 	pJson->nRecCount++;
25199 	VmJsonEncode(pValue, pJson);
25200 	pJson->nRecCount--;
25201 	pJson->isFirst = 0;
25202 	return JX9_OK;
25203 }
25204 /*
25205  * The following walker callback is invoked each time we need to encode
25206  * a object instance [i.e: Object in the JX9 jargon] to JSON.
25207  */
VmJsonObjectEncode(jx9_value * pKey,jx9_value * pValue,void * pUserData)25208 static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
25209 {
25210 	json_private_data *pJson = (json_private_data *)pUserData;
25211 	const char *zKey;
25212 	int nByte;
25213 	if( pJson->nRecCount > 31 ){
25214 		/* Recursion limit reached, return immediately */
25215 		return JX9_OK;
25216 	}
25217 	if( !pJson->isFirst ){
25218 		/* Append the colon first */
25219 		SyBlobAppend(pJson->pOut,",",sizeof(char));
25220 	}
25221 	/* Extract a string representation of the key */
25222 	zKey = jx9_value_to_string(pKey, &nByte);
25223 	/* Append the key and the double colon */
25224 	if( nByte > 0 ){
25225 		SyBlobAppend(pJson->pOut,"\"",sizeof(char));
25226 		SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
25227 		SyBlobAppend(pJson->pOut,"\"",sizeof(char));
25228 	}else{
25229 		/* Can't happen */
25230 		SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
25231 	}
25232 	SyBlobAppend(pJson->pOut,":",sizeof(char));
25233 	/* Encode the value */
25234 	pJson->nRecCount++;
25235 	VmJsonEncode(pValue, pJson);
25236 	pJson->nRecCount--;
25237 	pJson->isFirst = 0;
25238 	return JX9_OK;
25239 }
25240 /*
25241  *  Returns a string containing the JSON representation of value.
25242  *  In other words, perform the serialization of the given JSON object.
25243  */
jx9JsonSerialize(jx9_value * pValue,SyBlob * pOut)25244 JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
25245 {
25246 	json_private_data sJson;
25247 	/* Prepare the JSON data */
25248 	sJson.nRecCount = 0;
25249 	sJson.pOut = pOut;
25250 	sJson.isFirst = 1;
25251 	sJson.iFlags = 0;
25252 	/* Perform the encoding operation */
25253 	VmJsonEncode(pValue, &sJson);
25254 	/* All done */
25255 	return JX9_OK;
25256 }
25257 /* Possible tokens from the JSON tokenization process */
25258 #define JSON_TK_TRUE    0x001 /* Boolean true */
25259 #define JSON_TK_FALSE   0x002 /* Boolean false */
25260 #define JSON_TK_STR     0x004 /* String enclosed in double quotes */
25261 #define JSON_TK_NULL    0x008 /* null */
25262 #define JSON_TK_NUM     0x010 /* Numeric */
25263 #define JSON_TK_OCB     0x020 /* Open curly braces '{' */
25264 #define JSON_TK_CCB     0x040 /* Closing curly braces '}' */
25265 #define JSON_TK_OSB     0x080 /* Open square bracke '[' */
25266 #define JSON_TK_CSB     0x100 /* Closing square bracket ']' */
25267 #define JSON_TK_COLON   0x200 /* Single colon ':' */
25268 #define JSON_TK_COMMA   0x400 /* Single comma ',' */
25269 #define JSON_TK_ID      0x800 /* ID */
25270 #define JSON_TK_INVALID 0x1000 /* Unexpected token */
25271 /*
25272  * Tokenize an entire JSON input.
25273  * Get a single low-level token from the input file.
25274  * Update the stream pointer so that it points to the first
25275  * character beyond the extracted token.
25276  */
VmJsonTokenize(SyStream * pStream,SyToken * pToken,void * pUserData,void * pCtxData)25277 static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
25278 {
25279 	int *pJsonErr = (int *)pUserData;
25280 	SyString *pStr;
25281 	int c;
25282 	/* Ignore leading white spaces */
25283 	while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
25284 		/* Advance the stream cursor */
25285 		if( pStream->zText[0] == '\n' ){
25286 			/* Update line counter */
25287 			pStream->nLine++;
25288 		}
25289 		pStream->zText++;
25290 	}
25291 	if( pStream->zText >= pStream->zEnd ){
25292 		/* End of input reached */
25293 		SXUNUSED(pCtxData); /* cc warning */
25294 		return SXERR_EOF;
25295 	}
25296 	/* Record token starting position and line */
25297 	pToken->nLine = pStream->nLine;
25298 	pToken->pUserData = 0;
25299 	pStr = &pToken->sData;
25300 	SyStringInitFromBuf(pStr, pStream->zText, 0);
25301 	if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
25302 		/* The following code fragment is taken verbatim from the xPP source tree.
25303 		 * xPP is a modern embeddable macro processor with advanced features useful for
25304 		 * application seeking for a production quality, ready to use macro processor.
25305 		 * xPP is a widely used library developed and maintened by Symisc Systems.
25306 		 * You can reach the xPP home page by following this link:
25307 		 * http://xpp.symisc.net/
25308 		 */
25309 		const unsigned char *zIn;
25310 		/* Isolate UTF-8 or alphanumeric stream */
25311 		if( pStream->zText[0] < 0xc0 ){
25312 			pStream->zText++;
25313 		}
25314 		for(;;){
25315 			zIn = pStream->zText;
25316 			if( zIn[0] >= 0xc0 ){
25317 				zIn++;
25318 				/* UTF-8 stream */
25319 				while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
25320 					zIn++;
25321 				}
25322 			}
25323 			/* Skip alphanumeric stream */
25324 			while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
25325 				zIn++;
25326 			}
25327 			if( zIn == pStream->zText ){
25328 				/* Not an UTF-8 or alphanumeric stream */
25329 				break;
25330 			}
25331 			/* Synchronize pointers */
25332 			pStream->zText = zIn;
25333 		}
25334 		/* Record token length */
25335 		pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25336 		/* A simple identifier */
25337 		pToken->nType = JSON_TK_ID;
25338 		if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
25339 			/* boolean true */
25340 			pToken->nType = JSON_TK_TRUE;
25341 		}else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
25342 			/* boolean false */
25343 			pToken->nType = JSON_TK_FALSE;
25344 		}else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
25345 			/* NULL */
25346 			pToken->nType = JSON_TK_NULL;
25347 		}
25348 		return SXRET_OK;
25349 	}
25350 	if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
25351 		|| pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
25352 			/* Single character */
25353 			c = pStream->zText[0];
25354 			/* Set token type */
25355 			switch(c){
25356 			case '[': pToken->nType = JSON_TK_OSB;   break;
25357 			case '{': pToken->nType = JSON_TK_OCB;   break;
25358 			case '}': pToken->nType = JSON_TK_CCB;   break;
25359 			case ']': pToken->nType = JSON_TK_CSB;   break;
25360 			case ':': pToken->nType = JSON_TK_COLON; break;
25361 			case ',': pToken->nType = JSON_TK_COMMA; break;
25362 			default:
25363 				break;
25364 			}
25365 			/* Advance the stream cursor */
25366 			pStream->zText++;
25367 	}else if( pStream->zText[0] == '"') {
25368 		/* JSON string */
25369 		pStream->zText++;
25370 		pStr->zString++;
25371 		/* Delimit the string */
25372 		while( pStream->zText < pStream->zEnd ){
25373 			if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
25374 				break;
25375 			}
25376 			if( pStream->zText[0] == '\n' ){
25377 				/* Update line counter */
25378 				pStream->nLine++;
25379 			}
25380 			pStream->zText++;
25381 		}
25382 		if( pStream->zText >= pStream->zEnd ){
25383 			/* Missing closing '"' */
25384 			pToken->nType = JSON_TK_INVALID;
25385 			*pJsonErr = SXERR_SYNTAX;
25386 		}else{
25387 			pToken->nType = JSON_TK_STR;
25388 			pStream->zText++; /* Jump the closing double quotes */
25389 		}
25390 	}else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25391 		/* Number */
25392 		pStream->zText++;
25393 		pToken->nType = JSON_TK_NUM;
25394 		while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25395 			pStream->zText++;
25396 		}
25397 		if( pStream->zText < pStream->zEnd ){
25398 			c = pStream->zText[0];
25399 			if( c == '.' ){
25400 					/* Real number */
25401 					pStream->zText++;
25402 					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25403 						pStream->zText++;
25404 					}
25405 					if( pStream->zText < pStream->zEnd ){
25406 						c = pStream->zText[0];
25407 						if( c=='e' || c=='E' ){
25408 							pStream->zText++;
25409 							if( pStream->zText < pStream->zEnd ){
25410 								c = pStream->zText[0];
25411 								if( c =='+' || c=='-' ){
25412 									pStream->zText++;
25413 								}
25414 								while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25415 									pStream->zText++;
25416 								}
25417 							}
25418 						}
25419 					}
25420 				}else if( c=='e' || c=='E' ){
25421 					/* Real number */
25422 					pStream->zText++;
25423 					if( pStream->zText < pStream->zEnd ){
25424 						c = pStream->zText[0];
25425 						if( c =='+' || c=='-' ){
25426 							pStream->zText++;
25427 						}
25428 						while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25429 							pStream->zText++;
25430 						}
25431 					}
25432 				}
25433 			}
25434 	}else{
25435 		/* Unexpected token */
25436 		pToken->nType = JSON_TK_INVALID;
25437 		/* Advance the stream cursor */
25438 		pStream->zText++;
25439 		*pJsonErr = SXERR_SYNTAX;
25440 		/* Abort processing immediatley */
25441 		return SXERR_ABORT;
25442 	}
25443 	/* record token length */
25444 	pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25445 	if( pToken->nType == JSON_TK_STR ){
25446 		pStr->nByte--;
25447 	}
25448 	/* Return to the lexer */
25449 	return SXRET_OK;
25450 }
25451 /*
25452  * JSON decoded input consumer callback signature.
25453  */
25454 typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *);
25455 /*
25456  * JSON decoder state is kept in the following structure.
25457  */
25458 typedef struct json_decoder json_decoder;
25459 struct json_decoder
25460 {
25461 	jx9_context *pCtx; /* Call context */
25462 	ProcJSONConsumer xConsumer; /* Consumer callback */
25463 	void *pUserData;   /* Last argument to xConsumer() */
25464 	int iFlags;        /* Configuration flags */
25465 	SyToken *pIn;      /* Token stream */
25466 	SyToken *pEnd;     /* End of the token stream */
25467 	int rec_count;     /* Current nesting level */
25468 	int *pErr;         /* JSON decoding error if any */
25469 };
25470 /* Forward declaration */
25471 static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
25472 /*
25473  * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
25474  * the result in the given jx9_value.
25475  */
VmJsonDequoteString(const SyString * pStr,jx9_value * pWorker)25476 static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
25477 {
25478 	const char *zIn = pStr->zString;
25479 	const char *zEnd = &pStr->zString[pStr->nByte];
25480 	const char *zCur;
25481 	int c;
25482 	/* Mark the value as a string */
25483 	jx9_value_string(pWorker, "", 0); /* Empty string */
25484 	for(;;){
25485 		zCur = zIn;
25486 		while( zIn < zEnd && zIn[0] != '\\' ){
25487 			zIn++;
25488 		}
25489 		if( zIn > zCur ){
25490 			/* Append chunk verbatim */
25491 			jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
25492 		}
25493 		zIn++;
25494 		if( zIn >= zEnd ){
25495 			/* End of the input reached */
25496 			break;
25497 		}
25498 		c = zIn[0];
25499 		/* Unescape the character */
25500 		switch(c){
25501 		case '"':  jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
25502 		case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
25503 		case 'n':  jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
25504 		case 'r':  jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
25505 		case 't':  jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
25506 		case 'f':  jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
25507 		default:
25508 			jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
25509 			break;
25510 		}
25511 		/* Advance the stream cursor */
25512 		zIn++;
25513 	}
25514 }
25515 /*
25516  * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
25517  * According to wikipedia
25518  * JSON's basic types are:
25519  *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
25520  *   String (double-quoted Unicode, with backslash escaping)
25521  *   Boolean (true or false)
25522  *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
25523  *    do not need to be of the same type)
25524  *   Object (an unordered collection of key:value pairs with the ':' character separating the key
25525  *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
25526  *     be distinct from each other)
25527  *   null (empty)
25528  * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
25529  */
VmJsonDecode(json_decoder * pDecoder,jx9_value * pArrayKey)25530 static sxi32 VmJsonDecode(
25531 	json_decoder *pDecoder, /* JSON decoder */
25532 	jx9_value *pArrayKey    /* Key for the decoded array */
25533 	){
25534 	jx9_value *pWorker; /* Worker variable */
25535 	sxi32 rc;
25536 	/* Check if we do not nest to much */
25537 	if( pDecoder->rec_count > 31 ){
25538 		/* Nesting limit reached, abort decoding immediately */
25539 		return SXERR_ABORT;
25540 	}
25541 	if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
25542 		/* Scalar value */
25543 		pWorker = jx9_context_new_scalar(pDecoder->pCtx);
25544 		if( pWorker == 0 ){
25545 			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25546 			/* Abort the decoding operation immediately */
25547 			return SXERR_ABORT;
25548 		}
25549 		/* Reflect the JSON image */
25550 		if( pDecoder->pIn->nType & JSON_TK_NULL ){
25551 			/* Nullify the value.*/
25552 			jx9_value_null(pWorker);
25553 		}else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
25554 			/* Boolean value */
25555 			jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
25556 		}else if( pDecoder->pIn->nType & JSON_TK_NUM ){
25557 			SyString *pStr = &pDecoder->pIn->sData;
25558 			/*
25559 			 * Numeric value.
25560 			 * Get a string representation first then try to get a numeric
25561 			 * value.
25562 			 */
25563 			jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
25564 			/* Obtain a numeric representation */
25565 			jx9MemObjToNumeric(pWorker);
25566 		}else if( pDecoder->pIn->nType & JSON_TK_ID ){
25567 			SyString *pStr = &pDecoder->pIn->sData;
25568 			jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
25569 		}else{
25570 			/* Dequote the string */
25571 			VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
25572 		}
25573 		/* Invoke the consumer callback */
25574 		rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
25575 		if( rc == SXERR_ABORT ){
25576 			return SXERR_ABORT;
25577 		}
25578 		/* All done, advance the stream cursor */
25579 		pDecoder->pIn++;
25580 	}else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
25581 		ProcJSONConsumer xOld;
25582 		void *pOld;
25583 		/* Array representation*/
25584 		pDecoder->pIn++;
25585 		/* Create a working array */
25586 		pWorker = jx9_context_new_array(pDecoder->pCtx);
25587 		if( pWorker == 0 ){
25588 			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25589 			/* Abort the decoding operation immediately */
25590 			return SXERR_ABORT;
25591 		}
25592 		/* Save the old consumer */
25593 		xOld = pDecoder->xConsumer;
25594 		pOld = pDecoder->pUserData;
25595 		/* Set the new consumer */
25596 		pDecoder->xConsumer = VmJsonArrayDecoder;
25597 		pDecoder->pUserData = pWorker;
25598 		/* Decode the array */
25599 		for(;;){
25600 			/* Jump trailing comma. Note that the standard JX9 engine will not let you
25601 			 * do this.
25602 			 */
25603 			while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
25604 				pDecoder->pIn++;
25605 			}
25606 			if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
25607 				if( pDecoder->pIn < pDecoder->pEnd ){
25608 					pDecoder->pIn++; /* Jump the trailing ']' */
25609 				}
25610 				break;
25611 			}
25612 			/* Recurse and decode the entry */
25613 			pDecoder->rec_count++;
25614 			rc = VmJsonDecode(pDecoder, 0);
25615 			pDecoder->rec_count--;
25616 			if( rc == SXERR_ABORT ){
25617 				/* Abort processing immediately */
25618 				return SXERR_ABORT;
25619 			}
25620 			/*The cursor is automatically advanced by the VmJsonDecode() function */
25621 			if( (pDecoder->pIn < pDecoder->pEnd) &&
25622 				((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
25623 					/* Unexpected token, abort immediatley */
25624 					*pDecoder->pErr = SXERR_SYNTAX;
25625 					return SXERR_ABORT;
25626 			}
25627 		}
25628 		/* Restore the old consumer */
25629 		pDecoder->xConsumer = xOld;
25630 		pDecoder->pUserData = pOld;
25631 		/* Invoke the old consumer on the decoded array */
25632 		xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
25633 	}else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
25634 		ProcJSONConsumer xOld;
25635 		jx9_value *pKey;
25636 		void *pOld;
25637 		/* Object representation*/
25638 		pDecoder->pIn++;
25639 		/* Create a working array */
25640 		pWorker = jx9_context_new_array(pDecoder->pCtx);
25641 		pKey = jx9_context_new_scalar(pDecoder->pCtx);
25642 		if( pWorker == 0 || pKey == 0){
25643 			jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
25644 			/* Abort the decoding operation immediately */
25645 			return SXERR_ABORT;
25646 		}
25647 		/* Save the old consumer */
25648 		xOld = pDecoder->xConsumer;
25649 		pOld = pDecoder->pUserData;
25650 		/* Set the new consumer */
25651 		pDecoder->xConsumer = VmJsonArrayDecoder;
25652 		pDecoder->pUserData = pWorker;
25653 		/* Decode the object */
25654 		for(;;){
25655 			/* Jump trailing comma. Note that the standard JX9 engine will not let you
25656 			 * do this.
25657 			 */
25658 			while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
25659 				pDecoder->pIn++;
25660 			}
25661 			if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
25662 				if( pDecoder->pIn < pDecoder->pEnd ){
25663 					pDecoder->pIn++; /* Jump the trailing ']' */
25664 				}
25665 				break;
25666 			}
25667 			if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
25668 				|| (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
25669 					/* Syntax error, return immediately */
25670 					*pDecoder->pErr = SXERR_SYNTAX;
25671 					return SXERR_ABORT;
25672 			}
25673 			if( pDecoder->pIn->nType & JSON_TK_ID ){
25674 				SyString *pStr = &pDecoder->pIn->sData;
25675 				jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
25676 			}else{
25677 				/* Dequote the key */
25678 				VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
25679 			}
25680 			/* Jump the key and the colon */
25681 			pDecoder->pIn += 2;
25682 			/* Recurse and decode the value */
25683 			pDecoder->rec_count++;
25684 			rc = VmJsonDecode(pDecoder, pKey);
25685 			pDecoder->rec_count--;
25686 			if( rc == SXERR_ABORT ){
25687 				/* Abort processing immediately */
25688 				return SXERR_ABORT;
25689 			}
25690 			/* Reset the internal buffer of the key */
25691 			jx9_value_reset_string_cursor(pKey);
25692 			/*The cursor is automatically advanced by the VmJsonDecode() function */
25693 		}
25694 		/* Restore the old consumer */
25695 		pDecoder->xConsumer = xOld;
25696 		pDecoder->pUserData = pOld;
25697 		/* Invoke the old consumer on the decoded object*/
25698 		xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
25699 		/* Release the key */
25700 		jx9_context_release_value(pDecoder->pCtx, pKey);
25701 	}else{
25702 		/* Unexpected token */
25703 		return SXERR_ABORT; /* Abort immediately */
25704 	}
25705 	/* Release the worker variable */
25706 	jx9_context_release_value(pDecoder->pCtx, pWorker);
25707 	return SXRET_OK;
25708 }
25709 /*
25710  * The following JSON decoder callback is invoked each time
25711  * a JSON array representation [i.e: [15, "hello", FALSE] ]
25712  * is being decoded.
25713  */
VmJsonArrayDecoder(jx9_context * pCtx,jx9_value * pKey,jx9_value * pWorker,void * pUserData)25714 static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
25715 {
25716 	jx9_value *pArray = (jx9_value *)pUserData;
25717 	/* Insert the entry */
25718 	jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
25719 	SXUNUSED(pCtx); /* cc warning */
25720 	/* All done */
25721 	return SXRET_OK;
25722 }
25723 /*
25724  * Standard JSON decoder callback.
25725  */
VmJsonDefaultDecoder(jx9_context * pCtx,jx9_value * pKey,jx9_value * pWorker,void * pUserData)25726 static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
25727 {
25728 	/* Return the value directly */
25729 	jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
25730 	SXUNUSED(pKey); /* cc warning */
25731 	SXUNUSED(pUserData);
25732 	/* All done */
25733 	return SXRET_OK;
25734 }
25735 /*
25736  * Exported JSON decoding interface
25737  */
jx9JsonDecode(jx9_context * pCtx,const char * zJSON,int nByte)25738 JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
25739 {
25740 	jx9_vm *pVm = pCtx->pVm;
25741 	json_decoder sDecoder;
25742 	SySet sToken;
25743 	SyLex sLex;
25744 	sxi32 rc;
25745 	/* Tokenize the input */
25746 	SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
25747 	rc = SXRET_OK;
25748 	SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
25749 	SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
25750 	if( rc != SXRET_OK ){
25751 		/* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
25752 		SyLexRelease(&sLex);
25753 		SySetRelease(&sToken);
25754 		/* return NULL */
25755 		jx9_result_null(pCtx);
25756 		return JX9_OK;
25757 	}
25758 	/* Fill the decoder */
25759 	sDecoder.pCtx = pCtx;
25760 	sDecoder.pErr = &rc;
25761 	sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
25762 	sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
25763 	sDecoder.iFlags = 0;
25764 	sDecoder.rec_count = 0;
25765 	/* Set a default consumer */
25766 	sDecoder.xConsumer = VmJsonDefaultDecoder;
25767 	sDecoder.pUserData = 0;
25768 	/* Decode the raw JSON input */
25769 	rc = VmJsonDecode(&sDecoder, 0);
25770 	if( rc == SXERR_ABORT ){
25771 		/*
25772 		 * Something goes wrong while decoding JSON input.Return NULL.
25773 		 */
25774 		jx9_result_null(pCtx);
25775 	}
25776 	/* Clean-up the mess left behind */
25777 	SyLexRelease(&sLex);
25778 	SySetRelease(&sToken);
25779 	/* All done */
25780 	return JX9_OK;
25781 }
25782 
25783 /* jx9_lex.c */
25784 /*
25785  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
25786  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
25787  * Version 1.7.2
25788  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
25789  * please contact Symisc Systems via:
25790  *       legal@symisc.net
25791  *       licensing@symisc.net
25792  *       contact@symisc.net
25793  * or visit:
25794  *      http://jx9.symisc.net/
25795  */
25796  /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
25797 #ifndef JX9_AMALGAMATION
25798 #include "jx9Int.h"
25799 #endif
25800 /* This file implements a thread-safe and full reentrant lexical analyzer for the Jx9 programming language */
25801 /* Forward declarations */
25802 static sxu32 keywordCode(const char *z,int n);
25803 static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken);
25804 /*
25805  * Tokenize a raw jx9 input.
25806  * Get a single low-level token from the input file. Update the stream pointer so that
25807  * it points to the first character beyond the extracted token.
25808  */
jx9TokenizeInput(SyStream * pStream,SyToken * pToken,void * pUserData,void * pCtxData)25809 static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData)
25810 {
25811 	SyString *pStr;
25812 	sxi32 rc;
25813 	/* Ignore leading white spaces */
25814 	while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
25815 		/* Advance the stream cursor */
25816 		if( pStream->zText[0] == '\n' ){
25817 			/* Update line counter */
25818 			pStream->nLine++;
25819 		}
25820 		pStream->zText++;
25821 	}
25822 	if( pStream->zText >= pStream->zEnd ){
25823 		/* End of input reached */
25824 		return SXERR_EOF;
25825 	}
25826 	/* Record token starting position and line */
25827 	pToken->nLine = pStream->nLine;
25828 	pToken->pUserData = 0;
25829 	pStr = &pToken->sData;
25830 	SyStringInitFromBuf(pStr, pStream->zText, 0);
25831 	if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
25832 		/* The following code fragment is taken verbatim from the xPP source tree.
25833 		 * xPP is a modern embeddable macro processor with advanced features useful for
25834 		 * application seeking for a production quality, ready to use macro processor.
25835 		 * xPP is a widely used library developed and maintened by Symisc Systems.
25836 		 * You can reach the xPP home page by following this link:
25837 		 * http://xpp.symisc.net/
25838 		 */
25839 		const unsigned char *zIn;
25840 		sxu32 nKeyword;
25841 		/* Isolate UTF-8 or alphanumeric stream */
25842 		if( pStream->zText[0] < 0xc0 ){
25843 			pStream->zText++;
25844 		}
25845 		for(;;){
25846 			zIn = pStream->zText;
25847 			if( zIn[0] >= 0xc0 ){
25848 				zIn++;
25849 				/* UTF-8 stream */
25850 				while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
25851 					zIn++;
25852 				}
25853 			}
25854 			/* Skip alphanumeric stream */
25855 			while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
25856 				zIn++;
25857 			}
25858 			if( zIn == pStream->zText ){
25859 				/* Not an UTF-8 or alphanumeric stream */
25860 				break;
25861 			}
25862 			/* Synchronize pointers */
25863 			pStream->zText = zIn;
25864 		}
25865 		/* Record token length */
25866 		pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25867 		nKeyword = keywordCode(pStr->zString, (int)pStr->nByte);
25868 		if( nKeyword != JX9_TK_ID ){
25869 			/* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */
25870 			pToken->nType = JX9_TK_KEYWORD;
25871 			pToken->pUserData = SX_INT_TO_PTR(nKeyword);
25872 		}else{
25873 			/* A simple identifier */
25874 			pToken->nType = JX9_TK_ID;
25875 		}
25876 	}else{
25877 		sxi32 c;
25878 		/* Non-alpha stream */
25879 		if( pStream->zText[0] == '#' ||
25880 			( pStream->zText[0] == '/' &&  &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){
25881 				pStream->zText++;
25882 				/* Inline comments */
25883 				while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){
25884 					pStream->zText++;
25885 				}
25886 				/* Tell the upper-layer to ignore this token */
25887 				return SXERR_CONTINUE;
25888 		}else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){
25889 			pStream->zText += 2;
25890 			/* Block comment */
25891 			while( pStream->zText < pStream->zEnd ){
25892 				if( pStream->zText[0] == '*' ){
25893 					if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/'  ){
25894 						break;
25895 					}
25896 				}
25897 				if( pStream->zText[0] == '\n' ){
25898 					pStream->nLine++;
25899 				}
25900 				pStream->zText++;
25901 			}
25902 			pStream->zText += 2;
25903 			/* Tell the upper-layer to ignore this token */
25904 			return SXERR_CONTINUE;
25905 		}else if( SyisDigit(pStream->zText[0]) ){
25906 			pStream->zText++;
25907 			/* Decimal digit stream */
25908 			while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25909 				pStream->zText++;
25910 			}
25911 			/* Mark the token as integer until we encounter a real number */
25912 			pToken->nType = JX9_TK_INTEGER;
25913 			if( pStream->zText < pStream->zEnd ){
25914 				c = pStream->zText[0];
25915 				if( c == '.' ){
25916 					/* Real number */
25917 					pStream->zText++;
25918 					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25919 						pStream->zText++;
25920 					}
25921 					if( pStream->zText < pStream->zEnd ){
25922 						c = pStream->zText[0];
25923 						if( c=='e' || c=='E' ){
25924 							pStream->zText++;
25925 							if( pStream->zText < pStream->zEnd ){
25926 								c = pStream->zText[0];
25927 								if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
25928 									pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
25929 										pStream->zText++;
25930 								}
25931 								while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25932 									pStream->zText++;
25933 								}
25934 							}
25935 						}
25936 					}
25937 					pToken->nType = JX9_TK_REAL;
25938 				}else if( c=='e' || c=='E' ){
25939 					SXUNUSED(pUserData); /* Prevent compiler warning */
25940 					SXUNUSED(pCtxData);
25941 					pStream->zText++;
25942 					if( pStream->zText < pStream->zEnd ){
25943 						c = pStream->zText[0];
25944 						if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
25945 							pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
25946 								pStream->zText++;
25947 						}
25948 						while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
25949 							pStream->zText++;
25950 						}
25951 					}
25952 					pToken->nType = JX9_TK_REAL;
25953 				}else if( c == 'x' || c == 'X' ){
25954 					/* Hex digit stream */
25955 					pStream->zText++;
25956 					while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){
25957 						pStream->zText++;
25958 					}
25959 				}else if(c  == 'b' || c == 'B' ){
25960 					/* Binary digit stream */
25961 					pStream->zText++;
25962 					while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){
25963 						pStream->zText++;
25964 					}
25965 				}
25966 			}
25967 			/* Record token length */
25968 			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
25969 			return SXRET_OK;
25970 		}
25971 		c = pStream->zText[0];
25972 		pStream->zText++; /* Advance the stream cursor */
25973 		/* Assume we are dealing with an operator*/
25974 		pToken->nType = JX9_TK_OP;
25975 		switch(c){
25976 		case '$': pToken->nType = JX9_TK_DOLLAR; break;
25977 		case '{': pToken->nType = JX9_TK_OCB;   break;
25978 		case '}': pToken->nType = JX9_TK_CCB;    break;
25979 		case '(': pToken->nType = JX9_TK_LPAREN; break;
25980 		case '[': pToken->nType |= JX9_TK_OSB;   break; /* Bitwise operation here, since the square bracket token '['
25981 														 * is a potential operator [i.e: subscripting] */
25982 		case ']': pToken->nType = JX9_TK_CSB;    break;
25983 		case ')': {
25984 			SySet *pTokSet = pStream->pSet;
25985 			/* Assemble type cast operators [i.e: (int), (float), (bool)...] */
25986 			if( pTokSet->nUsed >= 2 ){
25987 				SyToken *pTmp;
25988 				/* Peek the last recongnized token */
25989 				pTmp = (SyToken *)SySetPeek(pTokSet);
25990 				if( pTmp->nType & JX9_TK_KEYWORD ){
25991 					sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData);
25992 					if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){
25993 						pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2);
25994 						if( pTmp->nType & JX9_TK_LPAREN ){
25995 							/* Merge the three tokens '(' 'TYPE' ')' into a single one */
25996 							const char * zTypeCast = "(int)";
25997 							if( nID & JX9_TKWRD_FLOAT ){
25998 								zTypeCast = "(float)";
25999 							}else if( nID & JX9_TKWRD_BOOL ){
26000 								zTypeCast = "(bool)";
26001 							}else if( nID & JX9_TKWRD_STRING ){
26002 								zTypeCast = "(string)";
26003 							}
26004 							/* Reflect the change */
26005 							pToken->nType = JX9_TK_OP;
26006 							SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast));
26007 							/* Save the instance associated with the type cast operator */
26008 							pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0);
26009 							/* Remove the two previous tokens */
26010 							pTokSet->nUsed -= 2;
26011 							return SXRET_OK;
26012 						}
26013 					}
26014 				}
26015 			}
26016 			pToken->nType = JX9_TK_RPAREN;
26017 			break;
26018 				  }
26019 		case '\'':{
26020 			/* Single quoted string */
26021 			pStr->zString++;
26022 			while( pStream->zText < pStream->zEnd ){
26023 				if( pStream->zText[0] == '\''  ){
26024 					if( pStream->zText[-1] != '\\' ){
26025 						break;
26026 					}else{
26027 						const unsigned char *zPtr = &pStream->zText[-2];
26028 						sxi32 i = 1;
26029 						while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
26030 							zPtr--;
26031 							i++;
26032 						}
26033 						if((i&1)==0){
26034 							break;
26035 						}
26036 					}
26037 				}
26038 				if( pStream->zText[0] == '\n' ){
26039 					pStream->nLine++;
26040 				}
26041 				pStream->zText++;
26042 			}
26043 			/* Record token length and type */
26044 			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26045 			pToken->nType = JX9_TK_SSTR;
26046 			/* Jump the trailing single quote */
26047 			pStream->zText++;
26048 			return SXRET_OK;
26049 				  }
26050 		case '"':{
26051 			sxi32 iNest;
26052 			/* Double quoted string */
26053 			pStr->zString++;
26054 			while( pStream->zText < pStream->zEnd ){
26055 				if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){
26056 					iNest = 1;
26057 					pStream->zText++;
26058 					/* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */
26059 					while(pStream->zText < pStream->zEnd ){
26060 						if( pStream->zText[0] == '{' ){
26061 							iNest++;
26062 						}else if (pStream->zText[0] == '}' ){
26063 							iNest--;
26064 							if( iNest <= 0 ){
26065 								pStream->zText++;
26066 								break;
26067 							}
26068 						}else if( pStream->zText[0] == '\n' ){
26069 							pStream->nLine++;
26070 						}
26071 						pStream->zText++;
26072 					}
26073 					if( pStream->zText >= pStream->zEnd ){
26074 						break;
26075 					}
26076 				}
26077 				if( pStream->zText[0] == '"' ){
26078 					if( pStream->zText[-1] != '\\' ){
26079 						break;
26080 					}else{
26081 						const unsigned char *zPtr = &pStream->zText[-2];
26082 						sxi32 i = 1;
26083 						while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
26084 							zPtr--;
26085 							i++;
26086 						}
26087 						if((i&1)==0){
26088 							break;
26089 						}
26090 					}
26091 				}
26092 				if( pStream->zText[0] == '\n' ){
26093 					pStream->nLine++;
26094 				}
26095 				pStream->zText++;
26096 			}
26097 			/* Record token length and type */
26098 			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26099 			pToken->nType = JX9_TK_DSTR;
26100 			/* Jump the trailing quote */
26101 			pStream->zText++;
26102 			return SXRET_OK;
26103 				  }
26104 		case ':':
26105 			pToken->nType = JX9_TK_COLON; /* Single colon */
26106 			break;
26107 		case ',': pToken->nType |= JX9_TK_COMMA;  break; /* Comma is also an operator */
26108 		case ';': pToken->nType = JX9_TK_SEMI;   break;
26109 			/* Handle combined operators [i.e: +=, ===, !=== ...] */
26110 		case '=':
26111 			pToken->nType |= JX9_TK_EQUAL;
26112 			if( pStream->zText < pStream->zEnd ){
26113 				if( pStream->zText[0] == '=' ){
26114 					pToken->nType &= ~JX9_TK_EQUAL;
26115 					/* Current operator: == */
26116 					pStream->zText++;
26117 					if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26118 						/* Current operator: === */
26119 						pStream->zText++;
26120 					}
26121 				}
26122 			}
26123 			break;
26124 		case '!':
26125 			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26126 				/* Current operator: != */
26127 				pStream->zText++;
26128 				if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26129 					/* Current operator: !== */
26130 					pStream->zText++;
26131 				}
26132 			}
26133 			break;
26134 		case '&':
26135 			pToken->nType |= JX9_TK_AMPER;
26136 			if( pStream->zText < pStream->zEnd ){
26137 				if( pStream->zText[0] == '&' ){
26138 					pToken->nType &= ~JX9_TK_AMPER;
26139 					/* Current operator: && */
26140 					pStream->zText++;
26141 				}else if( pStream->zText[0] == '=' ){
26142 					pToken->nType &= ~JX9_TK_AMPER;
26143 					/* Current operator: &= */
26144 					pStream->zText++;
26145 				}
26146 			}
26147 		case '.':
26148 			if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){
26149 				/* Concatenation operator: '..' or '.='  */
26150 				pStream->zText++;
26151 			}
26152 			break;
26153 		case '|':
26154 			if( pStream->zText < pStream->zEnd ){
26155 				if( pStream->zText[0] == '|' ){
26156 					/* Current operator: || */
26157 					pStream->zText++;
26158 				}else if( pStream->zText[0] == '=' ){
26159 					/* Current operator: |= */
26160 					pStream->zText++;
26161 				}
26162 			}
26163 			break;
26164 		case '+':
26165 			if( pStream->zText < pStream->zEnd ){
26166 				if( pStream->zText[0] == '+' ){
26167 					/* Current operator: ++ */
26168 					pStream->zText++;
26169 				}else if( pStream->zText[0] == '=' ){
26170 					/* Current operator: += */
26171 					pStream->zText++;
26172 				}
26173 			}
26174 			break;
26175 		case '-':
26176 			if( pStream->zText < pStream->zEnd ){
26177 				if( pStream->zText[0] == '-' ){
26178 					/* Current operator: -- */
26179 					pStream->zText++;
26180 				}else if( pStream->zText[0] == '=' ){
26181 					/* Current operator: -= */
26182 					pStream->zText++;
26183 				}else if( pStream->zText[0] == '>' ){
26184 					/* Current operator: -> */
26185 					pStream->zText++;
26186 				}
26187 			}
26188 			break;
26189 		case '*':
26190 			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26191 				/* Current operator: *= */
26192 				pStream->zText++;
26193 			}
26194 			break;
26195 		case '/':
26196 			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26197 				/* Current operator: /= */
26198 				pStream->zText++;
26199 			}
26200 			break;
26201 		case '%':
26202 			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26203 				/* Current operator: %= */
26204 				pStream->zText++;
26205 			}
26206 			break;
26207 		case '^':
26208 			if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26209 				/* Current operator: ^= */
26210 				pStream->zText++;
26211 			}
26212 			break;
26213 		case '<':
26214 			if( pStream->zText < pStream->zEnd ){
26215 				if( pStream->zText[0] == '<' ){
26216 					/* Current operator: << */
26217 					pStream->zText++;
26218 					if( pStream->zText < pStream->zEnd ){
26219 						if( pStream->zText[0] == '=' ){
26220 							/* Current operator: <<= */
26221 							pStream->zText++;
26222 						}else if( pStream->zText[0] == '<' ){
26223 							/* Current Token: <<<  */
26224 							pStream->zText++;
26225 							/* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */
26226 							rc = LexExtractNowdoc(&(*pStream), &(*pToken));
26227 							if( rc == SXRET_OK ){
26228 								/* Here/Now doc successfuly extracted */
26229 								return SXRET_OK;
26230 							}
26231 						}
26232 					}
26233 				}else if( pStream->zText[0] == '>' ){
26234 					/* Current operator: <> */
26235 					pStream->zText++;
26236 				}else if( pStream->zText[0] == '=' ){
26237 					/* Current operator: <= */
26238 					pStream->zText++;
26239 				}
26240 			}
26241 			break;
26242 		case '>':
26243 			if( pStream->zText < pStream->zEnd ){
26244 				if( pStream->zText[0] == '>' ){
26245 					/* Current operator: >> */
26246 					pStream->zText++;
26247 					if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
26248 						/* Current operator: >>= */
26249 						pStream->zText++;
26250 					}
26251 				}else if( pStream->zText[0] == '=' ){
26252 					/* Current operator: >= */
26253 					pStream->zText++;
26254 				}
26255 			}
26256 			break;
26257 		default:
26258 			break;
26259 		}
26260 		if( pStr->nByte <= 0 ){
26261 			/* Record token length */
26262 			pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
26263 		}
26264 		if( pToken->nType & JX9_TK_OP ){
26265 			const jx9_expr_op *pOp;
26266 			/* Check if the extracted token is an operator */
26267 			pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet));
26268 			if( pOp == 0 ){
26269 				/* Not an operator */
26270 				pToken->nType &= ~JX9_TK_OP;
26271 				if( pToken->nType <= 0 ){
26272 					pToken->nType = JX9_TK_OTHER;
26273 				}
26274 			}else{
26275 				/* Save the instance associated with this operator for later processing */
26276 				pToken->pUserData = (void *)pOp;
26277 			}
26278 		}
26279 	}
26280 	/* Tell the upper-layer to save the extracted token for later processing */
26281 	return SXRET_OK;
26282 }
26283 /***** This file contains automatically generated code ******
26284 **
26285 ** The code in this file has been automatically generated by
26286 **
26287 **     $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 <chm@symisc.net> $
26288 **
26289 ** The code in this file implements a function that determines whether
26290 ** or not a given identifier is really a JX9 keyword.  The same thing
26291 ** might be implemented more directly using a hand-written hash table.
26292 ** But by using this automatically generated code, the size of the code
26293 ** is substantially reduced.  This is important for embedded applications
26294 ** on platforms with limited memory.
26295 */
26296 /* Hash score: 35 */
keywordCode(const char * z,int n)26297 static sxu32 keywordCode(const char *z, int n)
26298 {
26299   /* zText[] encodes 188 bytes of keywords in 128 bytes */
26300   /*   printegereturnconstaticaselseifloatincludefaultDIEXITcontinue      */
26301   /*   diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch       */
26302   /*   uplink                                                             */
26303   static const char zText[127] = {
26304     'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s',
26305     't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i',
26306     'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c',
26307     'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P',
26308     'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o',
26309     'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r',
26310     't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n',
26311     'k',
26312   };
26313   static const unsigned char aHash[59] = {
26314        0,   0,   0,   0,  15,   0,  30,   0,   0,   2,  19,  18,   0,
26315        0,  10,   3,  12,   0,  28,  29,  23,   0,  13,  22,   0,   0,
26316       14,  24,  25,  31,  11,   0,   0,   0,   0,   1,   5,   0,   0,
26317       20,   0,  27,   9,   0,   0,   0,   8,   0,   0,  26,   6,   0,
26318        0,  17,   0,   0,   0,   0,   0,
26319   };
26320   static const unsigned char aNext[31] = {
26321        0,   0,   0,   0,   0,   0,   0,   0,   0,   4,   0,   0,   0,
26322        0,   0,   0,   0,   0,   0,   0,   0,   0,  16,   0,  21,   7,
26323        0,   0,   0,   0,   0,
26324   };
26325   static const unsigned char aLen[31] = {
26326        5,   7,   3,   6,   5,   6,   4,   2,   6,   4,   2,   5,   7,
26327        7,   3,   4,   8,   3,   5,   2,   5,   4,   7,   5,   3,   7,
26328        8,   6,   6,   6,   6,
26329   };
26330   static const sxu16 aOffset[31] = {
26331        0,   2,   2,   8,  14,  17,  22,  23,  25,  25,  29,  30,  35,
26332       40,  47,  49,  53,  61,  64,  69,  71,  76,  76,  83,  88,  88,
26333       95, 103, 109, 115, 121,
26334   };
26335   static const sxu32 aCode[31] = {
26336     JX9_TKWRD_PRINT,   JX9_TKWRD_INT,      JX9_TKWRD_INT,     JX9_TKWRD_RETURN,   JX9_TKWRD_CONST,
26337     JX9_TKWRD_STATIC,  JX9_TKWRD_CASE,     JX9_TKWRD_AS,      JX9_TKWRD_ELIF,     JX9_TKWRD_ELSE,
26338     JX9_TKWRD_IF,      JX9_TKWRD_FLOAT,    JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT,  JX9_TKWRD_DIE,
26339     JX9_TKWRD_EXIT,    JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE,     JX9_TKWRD_WHILE,    JX9_TKWRD_AS,
26340     JX9_TKWRD_PRINT,   JX9_TKWRD_BOOL,     JX9_TKWRD_BOOL,    JX9_TKWRD_BREAK,    JX9_TKWRD_FOR,
26341     JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT,  JX9_TKWRD_STRING,  JX9_TKWRD_SWITCH,
26342     JX9_TKWRD_UPLINK,
26343   };
26344   int h, i;
26345   if( n<2 ) return JX9_TK_ID;
26346   h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59;
26347   for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
26348     if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){
26349        /* JX9_TKWRD_PRINT */
26350        /* JX9_TKWRD_INT */
26351        /* JX9_TKWRD_INT */
26352        /* JX9_TKWRD_RETURN */
26353        /* JX9_TKWRD_CONST */
26354        /* JX9_TKWRD_STATIC */
26355        /* JX9_TKWRD_CASE */
26356        /* JX9_TKWRD_AS */
26357        /* JX9_TKWRD_ELIF */
26358        /* JX9_TKWRD_ELSE */
26359        /* JX9_TKWRD_IF */
26360        /* JX9_TKWRD_FLOAT */
26361        /* JX9_TKWRD_INCLUDE */
26362        /* JX9_TKWRD_DEFAULT */
26363        /* JX9_TKWRD_DIE */
26364        /* JX9_TKWRD_EXIT */
26365        /* JX9_TKWRD_CONTINUE */
26366        /* JX9_TKWRD_DIE */
26367        /* JX9_TKWRD_WHILE */
26368        /* JX9_TKWRD_AS */
26369        /* JX9_TKWRD_PRINT */
26370        /* JX9_TKWRD_BOOL */
26371        /* JX9_TKWRD_BOOL */
26372        /* JX9_TKWRD_BREAK */
26373        /* JX9_TKWRD_FOR */
26374        /* JX9_TKWRD_FOREACH */
26375        /* JX9_TKWRD_FUNCTION */
26376        /* JX9_TKWRD_IMPORT */
26377        /* JX9_TKWRD_STRING */
26378        /* JX9_TKWRD_SWITCH */
26379        /* JX9_TKWRD_UPLINK */
26380       return aCode[i];
26381     }
26382   }
26383   return JX9_TK_ID;
26384 }
26385 /*
26386  * Extract a heredoc/nowdoc text from a raw JX9 input.
26387  * According to the JX9 language reference manual:
26388  *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
26389  *  is provided, then a newline. The string itself follows, and then the same identifier again
26390  *  to close the quotation.
26391  *  The closing identifier must begin in the first column of the line. Also, the identifier must
26392  *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric
26393  *  characters and underscores, and must start with a non-digit character or underscore.
26394  *  Heredoc text behaves just like a double-quoted string, without the double quotes.
26395  *  This means that quotes in a heredoc do not need to be escaped, but the escape codes listed
26396  *  above can still be used. Variables are expanded, but the same care must be taken when expressing
26397  *  complex variables inside a heredoc as with strings.
26398  *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
26399  *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
26400  *  The construct is ideal for embedding JX9 code or other large blocks of text without the need
26401  *  for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that
26402  *  it declares a block of text which is not for parsing.
26403  *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows
26404  *  is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc
26405  *  identifiers, especially those regarding the appearance of the closing identifier.
26406  */
LexExtractNowdoc(SyStream * pStream,SyToken * pToken)26407 static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken)
26408 {
26409 	const unsigned char *zIn  = pStream->zText;
26410 	const unsigned char *zEnd = pStream->zEnd;
26411 	const unsigned char *zPtr;
26412 	SyString sDelim;
26413 	SyString sStr;
26414 	/* Jump leading white spaces */
26415 	while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
26416 		zIn++;
26417 	}
26418 	if( zIn >= zEnd ){
26419 		/* A simple symbol, return immediately */
26420 		return SXERR_CONTINUE;
26421 	}
26422 	if( zIn[0] == '\'' || zIn[0] == '"' ){
26423 		zIn++;
26424 	}
26425 	if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){
26426 		/* Invalid delimiter, return immediately */
26427 		return SXERR_CONTINUE;
26428 	}
26429 	/* Isolate the identifier */
26430 	sDelim.zString = (const char *)zIn;
26431 	for(;;){
26432 		zPtr = zIn;
26433 		/* Skip alphanumeric stream */
26434 		while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){
26435 			zPtr++;
26436 		}
26437 		if( zPtr < zEnd && zPtr[0] >= 0xc0 ){
26438 			zPtr++;
26439 			/* UTF-8 stream */
26440 			while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){
26441 				zPtr++;
26442 			}
26443 		}
26444 		if( zPtr == zIn ){
26445 			/* Not an UTF-8 or alphanumeric stream */
26446 			break;
26447 		}
26448 		/* Synchronize pointers */
26449 		zIn = zPtr;
26450 	}
26451 	/* Get the identifier length */
26452 	sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString);
26453 	if( zIn[0] == '"' || zIn[0] == '\'' ){
26454 		/* Jump the trailing single quote */
26455 		zIn++;
26456 	}
26457 	/* Jump trailing white spaces */
26458 	while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
26459 		zIn++;
26460 	}
26461 	if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){
26462 		/* Invalid syntax */
26463 		return SXERR_CONTINUE;
26464 	}
26465 	pStream->nLine++; /* Increment line counter */
26466 	zIn++;
26467 	/* Isolate the delimited string */
26468 	sStr.zString = (const char *)zIn;
26469 	/* Go and found the closing delimiter */
26470 	for(;;){
26471 		/* Synchronize with the next line */
26472 		while( zIn < zEnd && zIn[0] != '\n' ){
26473 			zIn++;
26474 		}
26475 		if( zIn >= zEnd ){
26476 			/* End of the input reached, break immediately */
26477 			pStream->zText = pStream->zEnd;
26478 			break;
26479 		}
26480 		pStream->nLine++; /* Increment line counter */
26481 		zIn++;
26482 		if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){
26483 			zPtr = &zIn[sDelim.nByte];
26484 			while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
26485 				zPtr++;
26486 			}
26487 			if( zPtr >= zEnd ){
26488 				/* End of input */
26489 				pStream->zText = zPtr;
26490 				break;
26491 			}
26492 			if( zPtr[0] == ';' ){
26493 				const unsigned char *zCur = zPtr;
26494 				zPtr++;
26495 				while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
26496 					zPtr++;
26497 				}
26498 				if( zPtr >= zEnd || zPtr[0] == '\n' ){
26499 					/* Closing delimiter found, break immediately */
26500 					pStream->zText = zCur; /* Keep the semi-colon */
26501 					break;
26502 				}
26503 			}else if( zPtr[0] == '\n' ){
26504 				/* Closing delimiter found, break immediately */
26505 				pStream->zText = zPtr; /* Synchronize with the stream cursor */
26506 				break;
26507 			}
26508 			/* Synchronize pointers and continue searching */
26509 			zIn = zPtr;
26510 		}
26511 	} /* For(;;) */
26512 	/* Get the delimited string length */
26513 	sStr.nByte = (sxu32)((const char *)zIn-sStr.zString);
26514 	/* Record token type and length */
26515 	pToken->nType = JX9_TK_NOWDOC;
26516 	SyStringDupPtr(&pToken->sData, &sStr);
26517 	/* Remove trailing white spaces */
26518 	SyStringRightTrim(&pToken->sData);
26519 	/* All done */
26520 	return SXRET_OK;
26521 }
26522 /*
26523  * Tokenize a raw jx9 input.
26524  * This is the public tokenizer called by most code generator routines.
26525  */
jx9Tokenize(const char * zInput,sxu32 nLen,SySet * pOut)26526 JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut)
26527 {
26528 	SyLex sLexer;
26529 	sxi32 rc;
26530 	/* Initialize the lexer */
26531 	rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0);
26532 	if( rc != SXRET_OK ){
26533 		return rc;
26534 	}
26535 	/* Tokenize input */
26536 	rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0);
26537 	/* Release the lexer */
26538 	SyLexRelease(&sLexer);
26539 	/* Tokenization result */
26540 	return rc;
26541 }
26542 
26543 /* jx9_lib.c */
26544 /*
26545  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
26546  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
26547  * Version 1.7.2
26548  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
26549  * please contact Symisc Systems via:
26550  *       legal@symisc.net
26551  *       licensing@symisc.net
26552  *       contact@symisc.net
26553  * or visit:
26554  *      http://jx9.symisc.net/
26555  */
26556  /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable <chm@symisc.net> $ */
26557 /*
26558  * Symisc Run-Time API: A modern thread safe replacement of the standard libc
26559  * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/
26560  *
26561  * The Symisc Run-Time API is an independent project developed by symisc systems
26562  * internally as a secure replacement of the standard libc.
26563  * The library is re-entrant, thread-safe and platform independent.
26564  */
26565 #ifndef JX9_AMALGAMATION
26566 #include "jx9Int.h"
26567 #endif
26568 #if defined(__WINNT__)
26569 #include <Windows.h>
26570 #else
26571 #include <stdlib.h>
26572 #endif
26573 #if defined(JX9_ENABLE_THREADS)
26574 /* SyRunTimeApi: sxmutex.c */
26575 #if defined(__WINNT__)
26576 struct SyMutex
26577 {
26578 	CRITICAL_SECTION sMutex;
26579 	sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */
26580 };
26581 /* Preallocated static mutex */
26582 static SyMutex aStaticMutexes[] = {
26583 		{{0}, SXMUTEX_TYPE_STATIC_1},
26584 		{{0}, SXMUTEX_TYPE_STATIC_2},
26585 		{{0}, SXMUTEX_TYPE_STATIC_3},
26586 		{{0}, SXMUTEX_TYPE_STATIC_4},
26587 		{{0}, SXMUTEX_TYPE_STATIC_5},
26588 		{{0}, SXMUTEX_TYPE_STATIC_6}
26589 };
26590 static BOOL winMutexInit = FALSE;
26591 static LONG winMutexLock = 0;
26592 
WinMutexGlobaInit(void)26593 static sxi32 WinMutexGlobaInit(void)
26594 {
26595 	LONG rc;
26596 	rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
26597 	if ( rc == 0 ){
26598 		sxu32 n;
26599 		for( n = 0 ; n  < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
26600 			InitializeCriticalSection(&aStaticMutexes[n].sMutex);
26601 		}
26602 		winMutexInit = TRUE;
26603 	}else{
26604 		/* Someone else is doing this for us */
26605 		while( winMutexInit == FALSE ){
26606 			Sleep(1);
26607 		}
26608 	}
26609 	return SXRET_OK;
26610 }
WinMutexGlobalRelease(void)26611 static void WinMutexGlobalRelease(void)
26612 {
26613 	LONG rc;
26614 	rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
26615 	if( rc == 1 ){
26616 		/* The first to decrement to zero does the actual global release */
26617 		if( winMutexInit == TRUE ){
26618 			sxu32 n;
26619 			for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
26620 				DeleteCriticalSection(&aStaticMutexes[n].sMutex);
26621 			}
26622 			winMutexInit = FALSE;
26623 		}
26624 	}
26625 }
WinMutexNew(int nType)26626 static SyMutex * WinMutexNew(int nType)
26627 {
26628 	SyMutex *pMutex = 0;
26629 	if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
26630 		/* Allocate a new mutex */
26631 		pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
26632 		if( pMutex == 0 ){
26633 			return 0;
26634 		}
26635 		InitializeCriticalSection(&pMutex->sMutex);
26636 	}else{
26637 		/* Use a pre-allocated static mutex */
26638 		if( nType > SXMUTEX_TYPE_STATIC_6 ){
26639 			nType = SXMUTEX_TYPE_STATIC_6;
26640 		}
26641 		pMutex = &aStaticMutexes[nType - 3];
26642 	}
26643 	pMutex->nType = nType;
26644 	return pMutex;
26645 }
WinMutexRelease(SyMutex * pMutex)26646 static void WinMutexRelease(SyMutex *pMutex)
26647 {
26648 	if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
26649 		DeleteCriticalSection(&pMutex->sMutex);
26650 		HeapFree(GetProcessHeap(), 0, pMutex);
26651 	}
26652 }
WinMutexEnter(SyMutex * pMutex)26653 static void WinMutexEnter(SyMutex *pMutex)
26654 {
26655 	EnterCriticalSection(&pMutex->sMutex);
26656 }
WinMutexTryEnter(SyMutex * pMutex)26657 static sxi32 WinMutexTryEnter(SyMutex *pMutex)
26658 {
26659 #ifdef _WIN32_WINNT
26660 	BOOL rc;
26661 	/* Only WindowsNT platforms */
26662 	rc = TryEnterCriticalSection(&pMutex->sMutex);
26663 	if( rc ){
26664 		return SXRET_OK;
26665 	}else{
26666 		return SXERR_BUSY;
26667 	}
26668 #else
26669 	return SXERR_NOTIMPLEMENTED;
26670 #endif
26671 }
WinMutexLeave(SyMutex * pMutex)26672 static void WinMutexLeave(SyMutex *pMutex)
26673 {
26674 	LeaveCriticalSection(&pMutex->sMutex);
26675 }
26676 /* Export Windows mutex interfaces */
26677 static const SyMutexMethods sWinMutexMethods = {
26678 	WinMutexGlobaInit,  /* xGlobalInit() */
26679 	WinMutexGlobalRelease, /* xGlobalRelease() */
26680 	WinMutexNew,     /* xNew() */
26681 	WinMutexRelease, /* xRelease() */
26682 	WinMutexEnter,   /* xEnter() */
26683 	WinMutexTryEnter, /* xTryEnter() */
26684 	WinMutexLeave     /* xLeave() */
26685 };
SyMutexExportMethods(void)26686 JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26687 {
26688 	return &sWinMutexMethods;
26689 }
26690 #elif defined(__UNIXES__)
26691 #include <pthread.h>
26692 struct SyMutex
26693 {
26694 	pthread_mutex_t sMutex;
26695 	sxu32 nType;
26696 };
UnixMutexNew(int nType)26697 static SyMutex * UnixMutexNew(int nType)
26698 {
26699 	static SyMutex aStaticMutexes[] = {
26700 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1},
26701 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2},
26702 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3},
26703 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4},
26704 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5},
26705 		{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
26706 	};
26707 	SyMutex *pMutex;
26708 
26709 	if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
26710 		pthread_mutexattr_t sRecursiveAttr;
26711   		/* Allocate a new mutex */
26712   		pMutex = (SyMutex *)malloc(sizeof(SyMutex));
26713   		if( pMutex == 0 ){
26714   			return 0;
26715   		}
26716   		if( nType == SXMUTEX_TYPE_RECURSIVE ){
26717   			pthread_mutexattr_init(&sRecursiveAttr);
26718   			pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
26719   		}
26720   		pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 );
26721 		if(	nType == SXMUTEX_TYPE_RECURSIVE ){
26722    			pthread_mutexattr_destroy(&sRecursiveAttr);
26723 		}
26724 	}else{
26725 		/* Use a pre-allocated static mutex */
26726 		if( nType > SXMUTEX_TYPE_STATIC_6 ){
26727 			nType = SXMUTEX_TYPE_STATIC_6;
26728 		}
26729 		pMutex = &aStaticMutexes[nType - 3];
26730 	}
26731   pMutex->nType = nType;
26732 
26733   return pMutex;
26734 }
UnixMutexRelease(SyMutex * pMutex)26735 static void UnixMutexRelease(SyMutex *pMutex)
26736 {
26737 	if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
26738 		pthread_mutex_destroy(&pMutex->sMutex);
26739 		free(pMutex);
26740 	}
26741 }
UnixMutexEnter(SyMutex * pMutex)26742 static void UnixMutexEnter(SyMutex *pMutex)
26743 {
26744 	pthread_mutex_lock(&pMutex->sMutex);
26745 }
UnixMutexLeave(SyMutex * pMutex)26746 static void UnixMutexLeave(SyMutex *pMutex)
26747 {
26748 	pthread_mutex_unlock(&pMutex->sMutex);
26749 }
26750 /* Export pthread mutex interfaces */
26751 static const SyMutexMethods sPthreadMutexMethods = {
26752 	0, /* xGlobalInit() */
26753 	0, /* xGlobalRelease() */
26754 	UnixMutexNew,      /* xNew() */
26755 	UnixMutexRelease,  /* xRelease() */
26756 	UnixMutexEnter,    /* xEnter() */
26757 	0,                 /* xTryEnter() */
26758 	UnixMutexLeave     /* xLeave() */
26759 };
SyMutexExportMethods(void)26760 JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26761 {
26762 	return &sPthreadMutexMethods;
26763 }
26764 #else
26765 /* Host application must register their own mutex subsystem if the target
26766  * platform is not an UNIX-like or windows systems.
26767  */
26768 struct SyMutex
26769 {
26770 	sxu32 nType;
26771 };
DummyMutexNew(int nType)26772 static SyMutex * DummyMutexNew(int nType)
26773 {
26774 	static SyMutex sMutex;
26775 	SXUNUSED(nType);
26776 	return &sMutex;
26777 }
DummyMutexRelease(SyMutex * pMutex)26778 static void DummyMutexRelease(SyMutex *pMutex)
26779 {
26780 	SXUNUSED(pMutex);
26781 }
DummyMutexEnter(SyMutex * pMutex)26782 static void DummyMutexEnter(SyMutex *pMutex)
26783 {
26784 	SXUNUSED(pMutex);
26785 }
DummyMutexLeave(SyMutex * pMutex)26786 static void DummyMutexLeave(SyMutex *pMutex)
26787 {
26788 	SXUNUSED(pMutex);
26789 }
26790 /* Export the dummy mutex interfaces */
26791 static const SyMutexMethods sDummyMutexMethods = {
26792 	0, /* xGlobalInit() */
26793 	0, /* xGlobalRelease() */
26794 	DummyMutexNew,      /* xNew() */
26795 	DummyMutexRelease,  /* xRelease() */
26796 	DummyMutexEnter,    /* xEnter() */
26797 	0,                  /* xTryEnter() */
26798 	DummyMutexLeave     /* xLeave() */
26799 };
SyMutexExportMethods(void)26800 JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
26801 {
26802 	return &sDummyMutexMethods;
26803 }
26804 #endif /* __WINNT__ */
26805 #endif /* JX9_ENABLE_THREADS */
SyOSHeapAlloc(sxu32 nByte)26806 static void * SyOSHeapAlloc(sxu32 nByte)
26807 {
26808 	void *pNew;
26809 #if defined(__WINNT__)
26810 	pNew = HeapAlloc(GetProcessHeap(), 0, nByte);
26811 #else
26812 	pNew = malloc((size_t)nByte);
26813 #endif
26814 	return pNew;
26815 }
SyOSHeapRealloc(void * pOld,sxu32 nByte)26816 static void * SyOSHeapRealloc(void *pOld, sxu32 nByte)
26817 {
26818 	void *pNew;
26819 #if defined(__WINNT__)
26820 	pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte);
26821 #else
26822 	pNew = realloc(pOld, (size_t)nByte);
26823 #endif
26824 	return pNew;
26825 }
SyOSHeapFree(void * pPtr)26826 static void SyOSHeapFree(void *pPtr)
26827 {
26828 #if defined(__WINNT__)
26829 	HeapFree(GetProcessHeap(), 0, pPtr);
26830 #else
26831 	free(pPtr);
26832 #endif
26833 }
26834 /* SyRunTimeApi:sxstr.c */
SyStrlen(const char * zSrc)26835 JX9_PRIVATE sxu32 SyStrlen(const char *zSrc)
26836 {
26837 	register const char *zIn = zSrc;
26838 #if defined(UNTRUST)
26839 	if( zIn == 0 ){
26840 		return 0;
26841 	}
26842 #endif
26843 	for(;;){
26844 		if( !zIn[0] ){ break; } zIn++;
26845 		if( !zIn[0] ){ break; } zIn++;
26846 		if( !zIn[0] ){ break; } zIn++;
26847 		if( !zIn[0] ){ break; } zIn++;
26848 	}
26849 	return (sxu32)(zIn - zSrc);
26850 }
SyByteFind(const char * zStr,sxu32 nLen,sxi32 c,sxu32 * pPos)26851 JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
26852 {
26853 	const char *zIn = zStr;
26854 	const char *zEnd;
26855 
26856 	zEnd = &zIn[nLen];
26857 	for(;;){
26858 		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26859 		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26860 		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26861 		if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
26862 	}
26863 	return SXERR_NOTFOUND;
26864 }
26865 #ifndef JX9_DISABLE_BUILTIN_FUNC
SyByteFind2(const char * zStr,sxu32 nLen,sxi32 c,sxu32 * pPos)26866 JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
26867 {
26868 	const char *zIn = zStr;
26869 	const char *zEnd;
26870 
26871 	zEnd = &zIn[nLen - 1];
26872 	for( ;; ){
26873 		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26874 		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26875 		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26876 		if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
26877 	}
26878 	return SXERR_NOTFOUND;
26879 }
26880 #endif /* JX9_DISABLE_BUILTIN_FUNC */
SyByteListFind(const char * zSrc,sxu32 nLen,const char * zList,sxu32 * pFirstPos)26881 JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos)
26882 {
26883 	const char *zIn = zSrc;
26884 	const char *zPtr;
26885 	const char *zEnd;
26886 	sxi32 c;
26887 	zEnd = &zSrc[nLen];
26888 	for(;;){
26889 		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++;
26890 		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++;
26891 		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++;
26892 		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++;
26893 	}
26894 	return SXERR_NOTFOUND;
26895 }
26896 #ifndef JX9_DISABLE_BUILTIN_FUNC
SyStrncmp(const char * zLeft,const char * zRight,sxu32 nLen)26897 JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen)
26898 {
26899 	const unsigned char *zP = (const unsigned char *)zLeft;
26900 	const unsigned char *zQ = (const unsigned char *)zRight;
26901 
26902 	if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ)  ){
26903 			return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1;
26904 	}
26905 	if( nLen <= 0 ){
26906 		return 0;
26907 	}
26908 	for(;;){
26909 		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26910 		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26911 		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26912 		if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
26913 	}
26914 	return (sxi32)(zP[0] - zQ[0]);
26915 }
26916 #endif
SyStrnicmp(const char * zLeft,const char * zRight,sxu32 SLen)26917 JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen)
26918 {
26919   	register unsigned char *p = (unsigned char *)zLeft;
26920 	register unsigned char *q = (unsigned char *)zRight;
26921 
26922 	if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){
26923 		return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1;
26924 	}
26925 	for(;;){
26926 		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
26927 		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
26928 		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
26929 		if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
26930 
26931 	}
26932 	return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0]));
26933 }
Systrcpy(char * zDest,sxu32 nDestLen,const char * zSrc,sxu32 nLen)26934 JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen)
26935 {
26936 	unsigned char *zBuf = (unsigned char *)zDest;
26937 	unsigned char *zIn = (unsigned char *)zSrc;
26938 	unsigned char *zEnd;
26939 #if defined(UNTRUST)
26940 	if( zSrc == (const char *)zDest ){
26941 			return 0;
26942 	}
26943 #endif
26944 	if( nLen <= 0 ){
26945 		nLen = SyStrlen(zSrc);
26946 	}
26947 	zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */
26948 	for(;;){
26949 		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
26950 		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
26951 		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
26952 		if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
26953 	}
26954 	zBuf[0] = 0;
26955 	return (sxu32)(zBuf-(unsigned char *)zDest);
26956 }
26957 /* SyRunTimeApi:sxmem.c */
SyZero(void * pSrc,sxu32 nSize)26958 JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize)
26959 {
26960 	register unsigned char *zSrc = (unsigned char *)pSrc;
26961 	unsigned char *zEnd;
26962 #if defined(UNTRUST)
26963 	if( zSrc == 0 || nSize <= 0 ){
26964 		return ;
26965 	}
26966 #endif
26967 	zEnd = &zSrc[nSize];
26968 	for(;;){
26969 		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
26970 		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
26971 		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
26972 		if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
26973 	}
26974 }
SyMemcmp(const void * pB1,const void * pB2,sxu32 nSize)26975 JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize)
26976 {
26977 	sxi32 rc;
26978 	if( nSize <= 0 ){
26979 		return 0;
26980 	}
26981 	if( pB1 == 0 || pB2 == 0 ){
26982 		return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1);
26983 	}
26984 	SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc);
26985 	return rc;
26986 }
SyMemcpy(const void * pSrc,void * pDest,sxu32 nLen)26987 JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen)
26988 {
26989 	if( pSrc == 0 || pDest == 0 ){
26990 		return 0;
26991 	}
26992 	if( pSrc == (const void *)pDest ){
26993 		return nLen;
26994 	}
26995 	SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen);
26996 	return nLen;
26997 }
MemOSAlloc(sxu32 nBytes)26998 static void * MemOSAlloc(sxu32 nBytes)
26999 {
27000 	sxu32 *pChunk;
27001 	pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32));
27002 	if( pChunk == 0 ){
27003 		return 0;
27004 	}
27005 	pChunk[0] = nBytes;
27006 	return (void *)&pChunk[1];
27007 }
MemOSRealloc(void * pOld,sxu32 nBytes)27008 static void * MemOSRealloc(void *pOld, sxu32 nBytes)
27009 {
27010 	sxu32 *pOldChunk;
27011 	sxu32 *pChunk;
27012 	pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32));
27013 	if( pOldChunk[0] >= nBytes ){
27014 		return pOld;
27015 	}
27016 	pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32));
27017 	if( pChunk == 0 ){
27018 		return 0;
27019 	}
27020 	pChunk[0] = nBytes;
27021 	return (void *)&pChunk[1];
27022 }
MemOSFree(void * pBlock)27023 static void MemOSFree(void *pBlock)
27024 {
27025 	void *pChunk;
27026 	pChunk = (void *)(((char *)pBlock)-sizeof(sxu32));
27027 	SyOSHeapFree(pChunk);
27028 }
MemOSChunkSize(void * pBlock)27029 static sxu32 MemOSChunkSize(void *pBlock)
27030 {
27031 	sxu32 *pChunk;
27032 	pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32));
27033 	return pChunk[0];
27034 }
27035 /* Export OS allocation methods */
27036 static const SyMemMethods sOSAllocMethods = {
27037 	MemOSAlloc,
27038 	MemOSRealloc,
27039 	MemOSFree,
27040 	MemOSChunkSize,
27041 	0,
27042 	0,
27043 	0
27044 };
MemBackendAlloc(SyMemBackend * pBackend,sxu32 nByte)27045 static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
27046 {
27047 	SyMemBlock *pBlock;
27048 	sxi32 nRetry = 0;
27049 
27050 	/* Append an extra block so we can tracks allocated chunks and avoid memory
27051 	 * leaks.
27052 	 */
27053 	nByte += sizeof(SyMemBlock);
27054 	for(;;){
27055 		pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte);
27056 		if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY
27057 			|| SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
27058 				break;
27059 		}
27060 		nRetry++;
27061 	}
27062 	if( pBlock  == 0 ){
27063 		return 0;
27064 	}
27065 	pBlock->pNext = pBlock->pPrev = 0;
27066 	/* Link to the list of already tracked blocks */
27067 	MACRO_LD_PUSH(pBackend->pBlocks, pBlock);
27068 #if defined(UNTRUST)
27069 	pBlock->nGuard = SXMEM_BACKEND_MAGIC;
27070 #endif
27071 	pBackend->nBlock++;
27072 	return (void *)&pBlock[1];
27073 }
SyMemBackendAlloc(SyMemBackend * pBackend,sxu32 nByte)27074 JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
27075 {
27076 	void *pChunk;
27077 #if defined(UNTRUST)
27078 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27079 		return 0;
27080 	}
27081 #endif
27082 	if( pBackend->pMutexMethods ){
27083 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27084 	}
27085 	pChunk = MemBackendAlloc(&(*pBackend), nByte);
27086 	if( pBackend->pMutexMethods ){
27087 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27088 	}
27089 	return pChunk;
27090 }
MemBackendRealloc(SyMemBackend * pBackend,void * pOld,sxu32 nByte)27091 static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27092 {
27093 	SyMemBlock *pBlock, *pNew, *pPrev, *pNext;
27094 	sxu32 nRetry = 0;
27095 
27096 	if( pOld == 0 ){
27097 		return MemBackendAlloc(&(*pBackend), nByte);
27098 	}
27099 	pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock));
27100 #if defined(UNTRUST)
27101 	if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
27102 		return 0;
27103 	}
27104 #endif
27105 	nByte += sizeof(SyMemBlock);
27106 	pPrev = pBlock->pPrev;
27107 	pNext = pBlock->pNext;
27108 	for(;;){
27109 		pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte);
27110 		if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY ||
27111 			SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
27112 				break;
27113 		}
27114 		nRetry++;
27115 	}
27116 	if( pNew == 0 ){
27117 		return 0;
27118 	}
27119 	if( pNew != pBlock ){
27120 		if( pPrev == 0 ){
27121 			pBackend->pBlocks = pNew;
27122 		}else{
27123 			pPrev->pNext = pNew;
27124 		}
27125 		if( pNext ){
27126 			pNext->pPrev = pNew;
27127 		}
27128 #if defined(UNTRUST)
27129 		pNew->nGuard = SXMEM_BACKEND_MAGIC;
27130 #endif
27131 	}
27132 	return (void *)&pNew[1];
27133 }
SyMemBackendRealloc(SyMemBackend * pBackend,void * pOld,sxu32 nByte)27134 JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27135 {
27136 	void *pChunk;
27137 #if defined(UNTRUST)
27138 	if( SXMEM_BACKEND_CORRUPT(pBackend)  ){
27139 		return 0;
27140 	}
27141 #endif
27142 	if( pBackend->pMutexMethods ){
27143 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27144 	}
27145 	pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte);
27146 	if( pBackend->pMutexMethods ){
27147 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27148 	}
27149 	return pChunk;
27150 }
MemBackendFree(SyMemBackend * pBackend,void * pChunk)27151 static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk)
27152 {
27153 	SyMemBlock *pBlock;
27154 	pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock));
27155 #if defined(UNTRUST)
27156 	if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
27157 		return SXERR_CORRUPT;
27158 	}
27159 #endif
27160 	/* Unlink from the list of active blocks */
27161 	if( pBackend->nBlock > 0 ){
27162 		/* Release the block */
27163 #if defined(UNTRUST)
27164 		/* Mark as stale block */
27165 		pBlock->nGuard = 0x635B;
27166 #endif
27167 		MACRO_LD_REMOVE(pBackend->pBlocks, pBlock);
27168 		pBackend->nBlock--;
27169 		pBackend->pMethods->xFree(pBlock);
27170 	}
27171 	return SXRET_OK;
27172 }
SyMemBackendFree(SyMemBackend * pBackend,void * pChunk)27173 JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk)
27174 {
27175 	sxi32 rc;
27176 #if defined(UNTRUST)
27177 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27178 		return SXERR_CORRUPT;
27179 	}
27180 #endif
27181 	if( pChunk == 0 ){
27182 		return SXRET_OK;
27183 	}
27184 	if( pBackend->pMutexMethods ){
27185 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27186 	}
27187 	rc = MemBackendFree(&(*pBackend), pChunk);
27188 	if( pBackend->pMutexMethods ){
27189 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27190 	}
27191 	return rc;
27192 }
27193 #if defined(JX9_ENABLE_THREADS)
SyMemBackendMakeThreadSafe(SyMemBackend * pBackend,const SyMutexMethods * pMethods)27194 JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods)
27195 {
27196 	SyMutex *pMutex;
27197 #if defined(UNTRUST)
27198 	if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){
27199 		return SXERR_CORRUPT;
27200 	}
27201 #endif
27202 	pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
27203 	if( pMutex == 0 ){
27204 		return SXERR_OS;
27205 	}
27206 	/* Attach the mutex to the memory backend */
27207 	pBackend->pMutex = pMutex;
27208 	pBackend->pMutexMethods = pMethods;
27209 	return SXRET_OK;
27210 }
SyMemBackendDisbaleMutexing(SyMemBackend * pBackend)27211 JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend)
27212 {
27213 #if defined(UNTRUST)
27214 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27215 		return SXERR_CORRUPT;
27216 	}
27217 #endif
27218 	if( pBackend->pMutex == 0 ){
27219 		/* There is no mutex subsystem at all */
27220 		return SXRET_OK;
27221 	}
27222 	SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
27223 	pBackend->pMutexMethods = 0;
27224 	pBackend->pMutex = 0;
27225 	return SXRET_OK;
27226 }
27227 #endif
27228 /*
27229  * Memory pool allocator
27230  */
27231 #define SXMEM_POOL_MAGIC		0xDEAD
27232 #define SXMEM_POOL_MAXALLOC		(1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR))
27233 #define SXMEM_POOL_MINALLOC		(1<<(SXMEM_POOL_INCR))
MemPoolBucketAlloc(SyMemBackend * pBackend,sxu32 nBucket)27234 static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket)
27235 {
27236 	char *zBucket, *zBucketEnd;
27237 	SyMemHeader *pHeader;
27238 	sxu32 nBucketSize;
27239 
27240 	/* Allocate one big block first */
27241 	zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC);
27242 	if( zBucket == 0 ){
27243 		return SXERR_MEM;
27244 	}
27245 	zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC];
27246 	/* Divide the big block into mini bucket pool */
27247 	nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
27248 	pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket;
27249 	for(;;){
27250 		if( &zBucket[nBucketSize] >= zBucketEnd ){
27251 			break;
27252 		}
27253 		pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize];
27254 		/* Advance the cursor to the next available chunk */
27255 		pHeader = pHeader->pNext;
27256 		zBucket += nBucketSize;
27257 	}
27258 	pHeader->pNext = 0;
27259 
27260 	return SXRET_OK;
27261 }
MemBackendPoolAlloc(SyMemBackend * pBackend,sxu32 nByte)27262 static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
27263 {
27264 	SyMemHeader *pBucket, *pNext;
27265 	sxu32 nBucketSize;
27266 	sxu32 nBucket;
27267 
27268 	if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){
27269 		/* Allocate a big chunk directly */
27270 		pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader));
27271 		if( pBucket == 0 ){
27272 			return 0;
27273 		}
27274 		/* Record as big block */
27275 		pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH;
27276 		return (void *)(pBucket+1);
27277 	}
27278 	/* Locate the appropriate bucket */
27279 	nBucket = 0;
27280 	nBucketSize = SXMEM_POOL_MINALLOC;
27281 	while( nByte + sizeof(SyMemHeader) > nBucketSize  ){
27282 		nBucketSize <<= 1;
27283 		nBucket++;
27284 	}
27285 	pBucket = pBackend->apPool[nBucket];
27286 	if( pBucket == 0 ){
27287 		sxi32 rc;
27288 		rc = MemPoolBucketAlloc(&(*pBackend), nBucket);
27289 		if( rc != SXRET_OK ){
27290 			return 0;
27291 		}
27292 		pBucket = pBackend->apPool[nBucket];
27293 	}
27294 	/* Remove from the free list */
27295 	pNext = pBucket->pNext;
27296 	pBackend->apPool[nBucket] = pNext;
27297 	/* Record bucket&magic number */
27298 	pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket;
27299 	return (void *)&pBucket[1];
27300 }
SyMemBackendPoolAlloc(SyMemBackend * pBackend,sxu32 nByte)27301 JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
27302 {
27303 	void *pChunk;
27304 #if defined(UNTRUST)
27305 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27306 		return 0;
27307 	}
27308 #endif
27309 	if( pBackend->pMutexMethods ){
27310 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27311 	}
27312 	pChunk = MemBackendPoolAlloc(&(*pBackend), nByte);
27313 	if( pBackend->pMutexMethods ){
27314 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27315 	}
27316 	return pChunk;
27317 }
MemBackendPoolFree(SyMemBackend * pBackend,void * pChunk)27318 static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
27319 {
27320 	SyMemHeader *pHeader;
27321 	sxu32 nBucket;
27322 	/* Get the corresponding bucket */
27323 	pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader));
27324 	/* Sanity check to avoid misuse */
27325 	if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
27326 		return SXERR_CORRUPT;
27327 	}
27328 	nBucket = pHeader->nBucket & 0xFFFF;
27329 	if( nBucket == SXU16_HIGH ){
27330 		/* Free the big block */
27331 		MemBackendFree(&(*pBackend), pHeader);
27332 	}else{
27333 		/* Return to the free list */
27334 		pHeader->pNext = pBackend->apPool[nBucket & 0x0f];
27335 		pBackend->apPool[nBucket & 0x0f] = pHeader;
27336 	}
27337 	return SXRET_OK;
27338 }
SyMemBackendPoolFree(SyMemBackend * pBackend,void * pChunk)27339 JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
27340 {
27341 	sxi32 rc;
27342 #if defined(UNTRUST)
27343 	if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){
27344 		return SXERR_CORRUPT;
27345 	}
27346 #endif
27347 	if( pBackend->pMutexMethods ){
27348 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27349 	}
27350 	rc = MemBackendPoolFree(&(*pBackend), pChunk);
27351 	if( pBackend->pMutexMethods ){
27352 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27353 	}
27354 	return rc;
27355 }
27356 #if 0
27357 static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27358 {
27359 	sxu32 nBucket, nBucketSize;
27360 	SyMemHeader *pHeader;
27361 	void * pNew;
27362 
27363 	if( pOld == 0 ){
27364 		/* Allocate a new pool */
27365 		pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
27366 		return pNew;
27367 	}
27368 	/* Get the corresponding bucket */
27369 	pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader));
27370 	/* Sanity check to avoid misuse */
27371 	if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
27372 		return 0;
27373 	}
27374 	nBucket = pHeader->nBucket & 0xFFFF;
27375 	if( nBucket == SXU16_HIGH ){
27376 		/* Big block */
27377 		return MemBackendRealloc(&(*pBackend), pHeader, nByte);
27378 	}
27379 	nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
27380 	if( nBucketSize >= nByte + sizeof(SyMemHeader) ){
27381 		/* The old bucket can honor the requested size */
27382 		return pOld;
27383 	}
27384 	/* Allocate a new pool */
27385 	pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
27386 	if( pNew == 0 ){
27387 		return 0;
27388 	}
27389 	/* Copy the old data into the new block */
27390 	SyMemcpy(pOld, pNew, nBucketSize);
27391 	/* Free the stale block */
27392 	MemBackendPoolFree(&(*pBackend), pOld);
27393 	return pNew;
27394 }
27395 JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
27396 {
27397 	void *pChunk;
27398 #if defined(UNTRUST)
27399 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27400 		return 0;
27401 	}
27402 #endif
27403 	if( pBackend->pMutexMethods ){
27404 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27405 	}
27406 	pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte);
27407 	if( pBackend->pMutexMethods ){
27408 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27409 	}
27410 	return pChunk;
27411 }
27412 #endif
SyMemBackendInit(SyMemBackend * pBackend,ProcMemError xMemErr,void * pUserData)27413 JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData)
27414 {
27415 #if defined(UNTRUST)
27416 	if( pBackend == 0 ){
27417 		return SXERR_EMPTY;
27418 	}
27419 #endif
27420 	/* Zero the allocator first */
27421 	SyZero(&(*pBackend), sizeof(SyMemBackend));
27422 	pBackend->xMemError = xMemErr;
27423 	pBackend->pUserData = pUserData;
27424 	/* Switch to the OS memory allocator */
27425 	pBackend->pMethods = &sOSAllocMethods;
27426 	if( pBackend->pMethods->xInit ){
27427 		/* Initialize the backend  */
27428 		if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
27429 			return SXERR_ABORT;
27430 		}
27431 	}
27432 #if defined(UNTRUST)
27433 	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27434 #endif
27435 	return SXRET_OK;
27436 }
SyMemBackendInitFromOthers(SyMemBackend * pBackend,const SyMemMethods * pMethods,ProcMemError xMemErr,void * pUserData)27437 JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData)
27438 {
27439 #if defined(UNTRUST)
27440 	if( pBackend == 0 || pMethods == 0){
27441 		return SXERR_EMPTY;
27442 	}
27443 #endif
27444 	if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){
27445 		/* mandatory methods are missing */
27446 		return SXERR_INVALID;
27447 	}
27448 	/* Zero the allocator first */
27449 	SyZero(&(*pBackend), sizeof(SyMemBackend));
27450 	pBackend->xMemError = xMemErr;
27451 	pBackend->pUserData = pUserData;
27452 	/* Switch to the host application memory allocator */
27453 	pBackend->pMethods = pMethods;
27454 	if( pBackend->pMethods->xInit ){
27455 		/* Initialize the backend  */
27456 		if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
27457 			return SXERR_ABORT;
27458 		}
27459 	}
27460 #if defined(UNTRUST)
27461 	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27462 #endif
27463 	return SXRET_OK;
27464 }
SyMemBackendInitFromParent(SyMemBackend * pBackend,const SyMemBackend * pParent)27465 JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent)
27466 {
27467 	sxu8 bInheritMutex;
27468 #if defined(UNTRUST)
27469 	if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){
27470 		return SXERR_CORRUPT;
27471 	}
27472 #endif
27473 	/* Zero the allocator first */
27474 	SyZero(&(*pBackend), sizeof(SyMemBackend));
27475 	pBackend->pMethods  = pParent->pMethods;
27476 	pBackend->xMemError = pParent->xMemError;
27477 	pBackend->pUserData = pParent->pUserData;
27478 	bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE;
27479 	if( bInheritMutex ){
27480 		pBackend->pMutexMethods = pParent->pMutexMethods;
27481 		/* Create a private mutex */
27482 		pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST);
27483 		if( pBackend->pMutex ==  0){
27484 			return SXERR_OS;
27485 		}
27486 	}
27487 #if defined(UNTRUST)
27488 	pBackend->nMagic = SXMEM_BACKEND_MAGIC;
27489 #endif
27490 	return SXRET_OK;
27491 }
MemBackendRelease(SyMemBackend * pBackend)27492 static sxi32 MemBackendRelease(SyMemBackend *pBackend)
27493 {
27494 	SyMemBlock *pBlock, *pNext;
27495 
27496 	pBlock = pBackend->pBlocks;
27497 	for(;;){
27498 		if( pBackend->nBlock == 0 ){
27499 			break;
27500 		}
27501 		pNext  = pBlock->pNext;
27502 		pBackend->pMethods->xFree(pBlock);
27503 		pBlock = pNext;
27504 		pBackend->nBlock--;
27505 		/* LOOP ONE */
27506 		if( pBackend->nBlock == 0 ){
27507 			break;
27508 		}
27509 		pNext  = pBlock->pNext;
27510 		pBackend->pMethods->xFree(pBlock);
27511 		pBlock = pNext;
27512 		pBackend->nBlock--;
27513 		/* LOOP TWO */
27514 		if( pBackend->nBlock == 0 ){
27515 			break;
27516 		}
27517 		pNext  = pBlock->pNext;
27518 		pBackend->pMethods->xFree(pBlock);
27519 		pBlock = pNext;
27520 		pBackend->nBlock--;
27521 		/* LOOP THREE */
27522 		if( pBackend->nBlock == 0 ){
27523 			break;
27524 		}
27525 		pNext  = pBlock->pNext;
27526 		pBackend->pMethods->xFree(pBlock);
27527 		pBlock = pNext;
27528 		pBackend->nBlock--;
27529 		/* LOOP FOUR */
27530 	}
27531 	if( pBackend->pMethods->xRelease ){
27532 		pBackend->pMethods->xRelease(pBackend->pMethods->pUserData);
27533 	}
27534 	pBackend->pMethods = 0;
27535 	pBackend->pBlocks  = 0;
27536 #if defined(UNTRUST)
27537 	pBackend->nMagic = 0x2626;
27538 #endif
27539 	return SXRET_OK;
27540 }
SyMemBackendRelease(SyMemBackend * pBackend)27541 JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend)
27542 {
27543 	sxi32 rc;
27544 #if defined(UNTRUST)
27545 	if( SXMEM_BACKEND_CORRUPT(pBackend) ){
27546 		return SXERR_INVALID;
27547 	}
27548 #endif
27549 	if( pBackend->pMutexMethods ){
27550 		SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
27551 	}
27552 	rc = MemBackendRelease(&(*pBackend));
27553 	if( pBackend->pMutexMethods ){
27554 		SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
27555 		SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
27556 	}
27557 	return rc;
27558 }
SyMemBackendDup(SyMemBackend * pBackend,const void * pSrc,sxu32 nSize)27559 JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize)
27560 {
27561 	void *pNew;
27562 #if defined(UNTRUST)
27563 	if( pSrc == 0 || nSize <= 0 ){
27564 		return 0;
27565 	}
27566 #endif
27567 	pNew = SyMemBackendAlloc(&(*pBackend), nSize);
27568 	if( pNew ){
27569 		SyMemcpy(pSrc, pNew, nSize);
27570 	}
27571 	return pNew;
27572 }
SyMemBackendStrDup(SyMemBackend * pBackend,const char * zSrc,sxu32 nSize)27573 JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize)
27574 {
27575 	char *zDest;
27576 	zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1);
27577 	if( zDest ){
27578 		Systrcpy(zDest, nSize+1, zSrc, nSize);
27579 	}
27580 	return zDest;
27581 }
SyBlobInitFromBuf(SyBlob * pBlob,void * pBuffer,sxu32 nSize)27582 JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize)
27583 {
27584 #if defined(UNTRUST)
27585 	if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){
27586 		return SXERR_EMPTY;
27587 	}
27588 #endif
27589 	pBlob->pBlob = pBuffer;
27590 	pBlob->mByte = nSize;
27591 	pBlob->nByte = 0;
27592 	pBlob->pAllocator = 0;
27593 	pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC;
27594 	return SXRET_OK;
27595 }
SyBlobInit(SyBlob * pBlob,SyMemBackend * pAllocator)27596 JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator)
27597 {
27598 #if defined(UNTRUST)
27599 	if( pBlob == 0  ){
27600 		return SXERR_EMPTY;
27601 	}
27602 #endif
27603 	pBlob->pBlob = 0;
27604 	pBlob->mByte = pBlob->nByte	= 0;
27605 	pBlob->pAllocator = &(*pAllocator);
27606 	pBlob->nFlags = 0;
27607 	return SXRET_OK;
27608 }
SyBlobReadOnly(SyBlob * pBlob,const void * pData,sxu32 nByte)27609 JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte)
27610 {
27611 #if defined(UNTRUST)
27612 	if( pBlob == 0  ){
27613 		return SXERR_EMPTY;
27614 	}
27615 #endif
27616 	pBlob->pBlob = (void *)pData;
27617 	pBlob->nByte = nByte;
27618 	pBlob->mByte = 0;
27619 	pBlob->nFlags |= SXBLOB_RDONLY;
27620 	return SXRET_OK;
27621 }
27622 #ifndef SXBLOB_MIN_GROWTH
27623 #define SXBLOB_MIN_GROWTH 16
27624 #endif
BlobPrepareGrow(SyBlob * pBlob,sxu32 * pByte)27625 static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte)
27626 {
27627 	sxu32 nByte;
27628 	void *pNew;
27629 	nByte = *pByte;
27630 	if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){
27631 		if ( SyBlobFreeSpace(pBlob) < nByte ){
27632 			*pByte = SyBlobFreeSpace(pBlob);
27633 			if( (*pByte) == 0 ){
27634 				return SXERR_SHORT;
27635 			}
27636 		}
27637 		return SXRET_OK;
27638 	}
27639 	if( pBlob->nFlags & SXBLOB_RDONLY ){
27640 		/* Make a copy of the read-only item */
27641 		if( pBlob->nByte > 0 ){
27642 			pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte);
27643 			if( pNew == 0 ){
27644 				return SXERR_MEM;
27645 			}
27646 			pBlob->pBlob = pNew;
27647 			pBlob->mByte = pBlob->nByte;
27648 		}else{
27649 			pBlob->pBlob = 0;
27650 			pBlob->mByte = 0;
27651 		}
27652 		/* Remove the read-only flag */
27653 		pBlob->nFlags &= ~SXBLOB_RDONLY;
27654 	}
27655 	if( SyBlobFreeSpace(pBlob) >= nByte ){
27656 		return SXRET_OK;
27657 	}
27658 	if( pBlob->mByte > 0 ){
27659 		nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH;
27660 	}else if ( nByte < SXBLOB_MIN_GROWTH ){
27661 		nByte = SXBLOB_MIN_GROWTH;
27662 	}
27663 	pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte);
27664 	if( pNew == 0 ){
27665 		return SXERR_MEM;
27666 	}
27667 	pBlob->pBlob = pNew;
27668 	pBlob->mByte = nByte;
27669 	return SXRET_OK;
27670 }
SyBlobAppend(SyBlob * pBlob,const void * pData,sxu32 nSize)27671 JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize)
27672 {
27673 	sxu8 *zBlob;
27674 	sxi32 rc;
27675 	if( nSize < 1 ){
27676 		return SXRET_OK;
27677 	}
27678 	rc = BlobPrepareGrow(&(*pBlob), &nSize);
27679 	if( SXRET_OK != rc ){
27680 		return rc;
27681 	}
27682 	if( pData ){
27683 		zBlob = (sxu8 *)pBlob->pBlob ;
27684 		zBlob = &zBlob[pBlob->nByte];
27685 		pBlob->nByte += nSize;
27686 		SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize);
27687 	}
27688 	return SXRET_OK;
27689 }
SyBlobNullAppend(SyBlob * pBlob)27690 JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob)
27691 {
27692 	sxi32 rc;
27693 	sxu32 n;
27694 	n = pBlob->nByte;
27695 	rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char));
27696 	if (rc == SXRET_OK ){
27697 		pBlob->nByte = n;
27698 	}
27699 	return rc;
27700 }
SyBlobDup(SyBlob * pSrc,SyBlob * pDest)27701 JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest)
27702 {
27703 	sxi32 rc = SXRET_OK;
27704 	if( pSrc->nByte > 0 ){
27705 		rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte);
27706 	}
27707 	return rc;
27708 }
SyBlobReset(SyBlob * pBlob)27709 JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob)
27710 {
27711 	pBlob->nByte = 0;
27712 	if( pBlob->nFlags & SXBLOB_RDONLY ){
27713 		/* Read-only (Not malloced chunk) */
27714 		pBlob->pBlob = 0;
27715 		pBlob->mByte = 0;
27716 		pBlob->nFlags &= ~SXBLOB_RDONLY;
27717 	}
27718 	return SXRET_OK;
27719 }
SyBlobTruncate(SyBlob * pBlob,sxu32 nNewLen)27720 JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen)
27721 {
27722 	if( nNewLen < pBlob->nByte ){
27723 		pBlob->nByte = nNewLen;
27724 	}
27725 	return SXRET_OK;
27726 }
SyBlobRelease(SyBlob * pBlob)27727 JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob)
27728 {
27729 	if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){
27730 		SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob);
27731 	}
27732 	pBlob->pBlob = 0;
27733 	pBlob->nByte = pBlob->mByte = 0;
27734 	pBlob->nFlags = 0;
27735 	return SXRET_OK;
27736 }
27737 #ifndef JX9_DISABLE_BUILTIN_FUNC
SyBlobSearch(const void * pBlob,sxu32 nLen,const void * pPattern,sxu32 pLen,sxu32 * pOfft)27738 JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft)
27739 {
27740 	const char *zIn = (const char *)pBlob;
27741 	const char *zEnd;
27742 	sxi32 rc;
27743 	if( pLen > nLen ){
27744 		return SXERR_NOTFOUND;
27745 	}
27746 	zEnd = &zIn[nLen-pLen];
27747 	for(;;){
27748 		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++;
27749 		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++;
27750 		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++;
27751 		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++;
27752 	}
27753 	return SXERR_NOTFOUND;
27754 }
27755 #endif /* JX9_DISABLE_BUILTIN_FUNC */
27756 /* SyRunTimeApi:sxds.c */
SySetInit(SySet * pSet,SyMemBackend * pAllocator,sxu32 ElemSize)27757 JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize)
27758 {
27759 	pSet->nSize = 0 ;
27760 	pSet->nUsed = 0;
27761 	pSet->nCursor = 0;
27762 	pSet->eSize = ElemSize;
27763 	pSet->pAllocator = pAllocator;
27764 	pSet->pBase =  0;
27765 	pSet->pUserData = 0;
27766 	return SXRET_OK;
27767 }
SySetPut(SySet * pSet,const void * pItem)27768 JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem)
27769 {
27770 	unsigned char *zbase;
27771 	if( pSet->nUsed >= pSet->nSize ){
27772 		void *pNew;
27773 		if( pSet->pAllocator == 0 ){
27774 			return  SXERR_LOCKED;
27775 		}
27776 		if( pSet->nSize <= 0 ){
27777 			pSet->nSize = 4;
27778 		}
27779 		pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2);
27780 		if( pNew == 0 ){
27781 			return SXERR_MEM;
27782 		}
27783 		pSet->pBase = pNew;
27784 		pSet->nSize <<= 1;
27785 	}
27786 	zbase = (unsigned char *)pSet->pBase;
27787 	SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize);
27788 	pSet->nUsed++;
27789 	return SXRET_OK;
27790 }
SySetAlloc(SySet * pSet,sxi32 nItem)27791 JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem)
27792 {
27793 	if( pSet->nSize > 0 ){
27794 		return SXERR_LOCKED;
27795 	}
27796 	if( nItem < 8 ){
27797 		nItem = 8;
27798 	}
27799 	pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem);
27800 	if( pSet->pBase == 0 ){
27801 		return SXERR_MEM;
27802 	}
27803 	pSet->nSize = nItem;
27804 	return SXRET_OK;
27805 }
SySetReset(SySet * pSet)27806 JX9_PRIVATE sxi32 SySetReset(SySet *pSet)
27807 {
27808 	pSet->nUsed   = 0;
27809 	pSet->nCursor = 0;
27810 	return SXRET_OK;
27811 }
SySetResetCursor(SySet * pSet)27812 JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet)
27813 {
27814 	pSet->nCursor = 0;
27815 	return SXRET_OK;
27816 }
SySetGetNextEntry(SySet * pSet,void ** ppEntry)27817 JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry)
27818 {
27819 	register unsigned char *zSrc;
27820 	if( pSet->nCursor >= pSet->nUsed ){
27821 		/* Reset cursor */
27822 		pSet->nCursor = 0;
27823 		return SXERR_EOF;
27824 	}
27825 	zSrc = (unsigned char *)SySetBasePtr(pSet);
27826 	if( ppEntry ){
27827 		*ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize];
27828 	}
27829 	pSet->nCursor++;
27830 	return SXRET_OK;
27831 }
SySetRelease(SySet * pSet)27832 JX9_PRIVATE sxi32 SySetRelease(SySet *pSet)
27833 {
27834 	sxi32 rc = SXRET_OK;
27835 	if( pSet->pAllocator && pSet->pBase ){
27836 		rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase);
27837 	}
27838 	pSet->pBase = 0;
27839 	pSet->nUsed = 0;
27840 	pSet->nCursor = 0;
27841 	return rc;
27842 }
SySetPeek(SySet * pSet)27843 JX9_PRIVATE void * SySetPeek(SySet *pSet)
27844 {
27845 	const char *zBase;
27846 	if( pSet->nUsed <= 0 ){
27847 		return 0;
27848 	}
27849 	zBase = (const char *)pSet->pBase;
27850 	return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize];
27851 }
SySetPop(SySet * pSet)27852 JX9_PRIVATE void * SySetPop(SySet *pSet)
27853 {
27854 	const char *zBase;
27855 	void *pData;
27856 	if( pSet->nUsed <= 0 ){
27857 		return 0;
27858 	}
27859 	zBase = (const char *)pSet->pBase;
27860 	pSet->nUsed--;
27861 	pData =  (void *)&zBase[pSet->nUsed * pSet->eSize];
27862 	return pData;
27863 }
SySetAt(SySet * pSet,sxu32 nIdx)27864 JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx)
27865 {
27866 	const char *zBase;
27867 	if( nIdx >= pSet->nUsed ){
27868 		/* Out of range */
27869 		return 0;
27870 	}
27871 	zBase = (const char *)pSet->pBase;
27872 	return (void *)&zBase[nIdx * pSet->eSize];
27873 }
27874 /* Private hash entry */
27875 struct SyHashEntry_Pr
27876 {
27877 	const void *pKey; /* Hash key */
27878 	sxu32 nKeyLen;    /* Key length */
27879 	void *pUserData;  /* User private data */
27880 	/* Private fields */
27881 	sxu32 nHash;
27882 	SyHash *pHash;
27883 	SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */
27884 	SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */
27885 };
27886 #define INVALID_HASH(H) ((H)->apBucket == 0)
SyHashInit(SyHash * pHash,SyMemBackend * pAllocator,ProcHash xHash,ProcCmp xCmp)27887 JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp)
27888 {
27889 	SyHashEntry_Pr **apNew;
27890 #if defined(UNTRUST)
27891 	if( pHash == 0 ){
27892 		return SXERR_EMPTY;
27893 	}
27894 #endif
27895 	/* Allocate a new table */
27896 	apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
27897 	if( apNew == 0 ){
27898 		return SXERR_MEM;
27899 	}
27900 	SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
27901 	pHash->pAllocator = &(*pAllocator);
27902 	pHash->xHash = xHash ? xHash : SyBinHash;
27903 	pHash->xCmp = xCmp ? xCmp : SyMemcmp;
27904 	pHash->pCurrent = pHash->pList = 0;
27905 	pHash->nEntry = 0;
27906 	pHash->apBucket = apNew;
27907 	pHash->nBucketSize = SXHASH_BUCKET_SIZE;
27908 	return SXRET_OK;
27909 }
SyHashRelease(SyHash * pHash)27910 JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash)
27911 {
27912 	SyHashEntry_Pr *pEntry, *pNext;
27913 #if defined(UNTRUST)
27914 	if( INVALID_HASH(pHash)  ){
27915 		return SXERR_EMPTY;
27916 	}
27917 #endif
27918 	pEntry = pHash->pList;
27919 	for(;;){
27920 		if( pHash->nEntry == 0 ){
27921 			break;
27922 		}
27923 		pNext = pEntry->pNext;
27924 		SyMemBackendPoolFree(pHash->pAllocator, pEntry);
27925 		pEntry = pNext;
27926 		pHash->nEntry--;
27927 	}
27928 	if( pHash->apBucket ){
27929 		SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
27930 	}
27931 	pHash->apBucket = 0;
27932 	pHash->nBucketSize = 0;
27933 	pHash->pAllocator = 0;
27934 	return SXRET_OK;
27935 }
HashGetEntry(SyHash * pHash,const void * pKey,sxu32 nKeyLen)27936 static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
27937 {
27938 	SyHashEntry_Pr *pEntry;
27939 	sxu32 nHash;
27940 
27941 	nHash = pHash->xHash(pKey, nKeyLen);
27942 	pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)];
27943 	for(;;){
27944 		if( pEntry == 0 ){
27945 			break;
27946 		}
27947 		if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen &&
27948 			pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){
27949 				return pEntry;
27950 		}
27951 		pEntry = pEntry->pNextCollide;
27952 	}
27953 	/* Entry not found */
27954 	return 0;
27955 }
SyHashGet(SyHash * pHash,const void * pKey,sxu32 nKeyLen)27956 JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
27957 {
27958 	SyHashEntry_Pr *pEntry;
27959 #if defined(UNTRUST)
27960 	if( INVALID_HASH(pHash) ){
27961 		return 0;
27962 	}
27963 #endif
27964 	if( pHash->nEntry < 1 || nKeyLen < 1 ){
27965 		/* Don't bother hashing, return immediately */
27966 		return 0;
27967 	}
27968 	pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
27969 	if( pEntry == 0 ){
27970 		return 0;
27971 	}
27972 	return (SyHashEntry *)pEntry;
27973 }
HashDeleteEntry(SyHash * pHash,SyHashEntry_Pr * pEntry,void ** ppUserData)27974 static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData)
27975 {
27976 	sxi32 rc;
27977 	if( pEntry->pPrevCollide == 0 ){
27978 		pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide;
27979 	}else{
27980 		pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
27981 	}
27982 	if( pEntry->pNextCollide ){
27983 		pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
27984 	}
27985 	MACRO_LD_REMOVE(pHash->pList, pEntry);
27986 	pHash->nEntry--;
27987 	if( ppUserData ){
27988 		/* Write a pointer to the user data */
27989 		*ppUserData = pEntry->pUserData;
27990 	}
27991 	/* Release the entry */
27992 	rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry);
27993 	return rc;
27994 }
SyHashDeleteEntry(SyHash * pHash,const void * pKey,sxu32 nKeyLen,void ** ppUserData)27995 JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData)
27996 {
27997 	SyHashEntry_Pr *pEntry;
27998 	sxi32 rc;
27999 #if defined(UNTRUST)
28000 	if( INVALID_HASH(pHash) ){
28001 		return SXERR_CORRUPT;
28002 	}
28003 #endif
28004 	pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
28005 	if( pEntry == 0 ){
28006 		return SXERR_NOTFOUND;
28007 	}
28008 	rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData);
28009 	return rc;
28010 }
SyHashForEach(SyHash * pHash,sxi32 (* xStep)(SyHashEntry *,void *),void * pUserData)28011 JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData)
28012 {
28013 	SyHashEntry_Pr *pEntry;
28014 	sxi32 rc;
28015 	sxu32 n;
28016 #if defined(UNTRUST)
28017 	if( INVALID_HASH(pHash) || xStep == 0){
28018 		return 0;
28019 	}
28020 #endif
28021 	pEntry = pHash->pList;
28022 	for( n = 0 ; n < pHash->nEntry ; n++ ){
28023 		/* Invoke the callback */
28024 		rc = xStep((SyHashEntry *)pEntry, pUserData);
28025 		if( rc != SXRET_OK ){
28026 			return rc;
28027 		}
28028 		/* Point to the next entry */
28029 		pEntry = pEntry->pNext;
28030 	}
28031 	return SXRET_OK;
28032 }
HashGrowTable(SyHash * pHash)28033 static sxi32 HashGrowTable(SyHash *pHash)
28034 {
28035 	sxu32 nNewSize = pHash->nBucketSize * 2;
28036 	SyHashEntry_Pr *pEntry;
28037 	SyHashEntry_Pr **apNew;
28038 	sxu32 n, iBucket;
28039 
28040 	/* Allocate a new larger table */
28041 	apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *));
28042 	if( apNew == 0 ){
28043 		/* Not so fatal, simply a performance hit */
28044 		return SXRET_OK;
28045 	}
28046 	/* Zero the new table */
28047 	SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *));
28048 	/* Rehash all entries */
28049 	for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++  ){
28050 		pEntry->pNextCollide = pEntry->pPrevCollide = 0;
28051 		/* Install in the new bucket */
28052 		iBucket = pEntry->nHash & (nNewSize - 1);
28053 		pEntry->pNextCollide = apNew[iBucket];
28054 		if( apNew[iBucket] != 0 ){
28055 			apNew[iBucket]->pPrevCollide = pEntry;
28056 		}
28057 		apNew[iBucket] = pEntry;
28058 		/* Point to the next entry */
28059 		pEntry = pEntry->pNext;
28060 	}
28061 	/* Release the old table and reflect the change */
28062 	SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
28063 	pHash->apBucket = apNew;
28064 	pHash->nBucketSize = nNewSize;
28065 	return SXRET_OK;
28066 }
HashInsert(SyHash * pHash,SyHashEntry_Pr * pEntry)28067 static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry)
28068 {
28069 	sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1);
28070 	/* Insert the entry in its corresponding bcuket */
28071 	pEntry->pNextCollide = pHash->apBucket[iBucket];
28072 	if( pHash->apBucket[iBucket] != 0 ){
28073 		pHash->apBucket[iBucket]->pPrevCollide = pEntry;
28074 	}
28075 	pHash->apBucket[iBucket] = pEntry;
28076 	/* Link to the entry list */
28077 	MACRO_LD_PUSH(pHash->pList, pEntry);
28078 	if( pHash->nEntry == 0 ){
28079 		pHash->pCurrent = pHash->pList;
28080 	}
28081 	pHash->nEntry++;
28082 	return SXRET_OK;
28083 }
SyHashInsert(SyHash * pHash,const void * pKey,sxu32 nKeyLen,void * pUserData)28084 JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData)
28085 {
28086 	SyHashEntry_Pr *pEntry;
28087 	sxi32 rc;
28088 #if defined(UNTRUST)
28089 	if( INVALID_HASH(pHash) || pKey == 0 ){
28090 		return SXERR_CORRUPT;
28091 	}
28092 #endif
28093 	if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){
28094 		rc = HashGrowTable(&(*pHash));
28095 		if( rc != SXRET_OK ){
28096 			return rc;
28097 		}
28098 	}
28099 	/* Allocate a new hash entry */
28100 	pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr));
28101 	if( pEntry == 0 ){
28102 		return SXERR_MEM;
28103 	}
28104 	/* Zero the entry */
28105 	SyZero(pEntry, sizeof(SyHashEntry_Pr));
28106 	pEntry->pHash = pHash;
28107 	pEntry->pKey = pKey;
28108 	pEntry->nKeyLen = nKeyLen;
28109 	pEntry->pUserData = pUserData;
28110 	pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen);
28111 	/* Finally insert the entry in its corresponding bucket */
28112 	rc = HashInsert(&(*pHash), pEntry);
28113 	return rc;
28114 }
28115 /* SyRunTimeApi:sxutils.c */
SyStrIsNumeric(const char * zSrc,sxu32 nLen,sxu8 * pReal,const char ** pzTail)28116 JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char  **pzTail)
28117 {
28118 	const char *zCur, *zEnd;
28119 #ifdef UNTRUST
28120 	if( SX_EMPTY_STR(zSrc) ){
28121 		return SXERR_EMPTY;
28122 	}
28123 #endif
28124 	zEnd = &zSrc[nLen];
28125 	/* Jump leading white spaces */
28126 	while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0  && SyisSpace(zSrc[0]) ){
28127 		zSrc++;
28128 	}
28129 	if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28130 		zSrc++;
28131 	}
28132 	zCur = zSrc;
28133 	if( pReal ){
28134 		*pReal = FALSE;
28135 	}
28136 	for(;;){
28137 		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28138 		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28139 		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28140 		if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
28141 	};
28142 	if( zSrc < zEnd && zSrc > zCur ){
28143 		int c = zSrc[0];
28144 		if( c == '.' ){
28145 			zSrc++;
28146 			if( pReal ){
28147 				*pReal = TRUE;
28148 			}
28149 			if( pzTail ){
28150 				while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28151 					zSrc++;
28152 				}
28153 				if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){
28154 					zSrc++;
28155 					if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28156 						zSrc++;
28157 					}
28158 					while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28159 						zSrc++;
28160 					}
28161 				}
28162 			}
28163 		}else if( c == 'e' || c == 'E' ){
28164 			zSrc++;
28165 			if( pReal ){
28166 				*pReal = TRUE;
28167 			}
28168 			if( pzTail ){
28169 				if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
28170 					zSrc++;
28171 				}
28172 				while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
28173 					zSrc++;
28174 				}
28175 			}
28176 		}
28177 	}
28178 	if( pzTail ){
28179 		/* Point to the non numeric part */
28180 		*pzTail = zSrc;
28181 	}
28182 	return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
28183 }
28184 #define SXINT32_MIN_STR		"2147483648"
28185 #define SXINT32_MAX_STR		"2147483647"
28186 #define SXINT64_MIN_STR		"9223372036854775808"
28187 #define SXINT64_MAX_STR		"9223372036854775807"
SyStrToInt32(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28188 JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28189 {
28190 	int isNeg = FALSE;
28191 	const char *zEnd;
28192 	sxi32 nVal = 0;
28193 	sxi16 i;
28194 #if defined(UNTRUST)
28195 	if( SX_EMPTY_STR(zSrc) ){
28196 		if( pOutVal ){
28197 			*(sxi32 *)pOutVal = 0;
28198 		}
28199 		return SXERR_EMPTY;
28200 	}
28201 #endif
28202 	zEnd = &zSrc[nLen];
28203 	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28204 		zSrc++;
28205 	}
28206 	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28207 		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28208 		zSrc++;
28209 	}
28210 	/* Skip leading zero */
28211 	while(zSrc < zEnd && zSrc[0] == '0' ){
28212 		zSrc++;
28213 	}
28214 	i = 10;
28215 	if( (sxu32)(zEnd-zSrc) >= 10 ){
28216 		/* Handle overflow */
28217 		i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9;
28218 	}
28219 	for(;;){
28220 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28221 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28222 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28223 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28224 	}
28225 	/* Skip trailing spaces */
28226 	while(zSrc < zEnd && SyisSpace(zSrc[0])){
28227 		zSrc++;
28228 	}
28229 	if( zRest ){
28230 		*zRest = (char *)zSrc;
28231 	}
28232 	if( pOutVal ){
28233 		if( isNeg == TRUE && nVal != 0 ){
28234 			nVal = -nVal;
28235 		}
28236 		*(sxi32 *)pOutVal = nVal;
28237 	}
28238 	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28239 }
SyStrToInt64(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28240 JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28241 {
28242 	int isNeg = FALSE;
28243 	const char *zEnd;
28244 	sxi64 nVal;
28245 	sxi16 i;
28246 #if defined(UNTRUST)
28247 	if( SX_EMPTY_STR(zSrc) ){
28248 		if( pOutVal ){
28249 			*(sxi32 *)pOutVal = 0;
28250 		}
28251 		return SXERR_EMPTY;
28252 	}
28253 #endif
28254 	zEnd = &zSrc[nLen];
28255 	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28256 		zSrc++;
28257 	}
28258 	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28259 		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28260 		zSrc++;
28261 	}
28262 	/* Skip leading zero */
28263 	while(zSrc < zEnd && zSrc[0] == '0' ){
28264 		zSrc++;
28265 	}
28266 	i = 19;
28267 	if( (sxu32)(zEnd-zSrc) >= 19 ){
28268 		i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
28269 	}
28270 	nVal = 0;
28271 	for(;;){
28272 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28273 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28274 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28275 		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
28276 	}
28277 	/* Skip trailing spaces */
28278 	while(zSrc < zEnd && SyisSpace(zSrc[0])){
28279 		zSrc++;
28280 	}
28281 	if( zRest ){
28282 		*zRest = (char *)zSrc;
28283 	}
28284 	if( pOutVal ){
28285 		if( isNeg == TRUE && nVal != 0 ){
28286 			nVal = -nVal;
28287 		}
28288 		*(sxi64 *)pOutVal = nVal;
28289 	}
28290 	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28291 }
SyHexToint(sxi32 c)28292 JX9_PRIVATE sxi32 SyHexToint(sxi32 c)
28293 {
28294 	switch(c){
28295 	case '0': return 0;
28296 	case '1': return 1;
28297 	case '2': return 2;
28298 	case '3': return 3;
28299 	case '4': return 4;
28300 	case '5': return 5;
28301 	case '6': return 6;
28302 	case '7': return 7;
28303 	case '8': return 8;
28304 	case '9': return 9;
28305 	case 'A': case 'a': return 10;
28306 	case 'B': case 'b': return 11;
28307 	case 'C': case 'c': return 12;
28308 	case 'D': case 'd': return 13;
28309 	case 'E': case 'e': return 14;
28310 	case 'F': case 'f': return 15;
28311 	}
28312 	return -1;
28313 }
SyHexStrToInt64(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28314 JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28315 {
28316 	const char *zIn, *zEnd;
28317 	int isNeg = FALSE;
28318 	sxi64 nVal = 0;
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 == '-' || *zSrc == '+' ) ){
28332 		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28333 		zSrc++;
28334 	}
28335 	if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){
28336 		/* Bypass hex prefix */
28337 		zSrc += sizeof(char) * 2;
28338 	}
28339 	/* Skip leading zero */
28340 	while(zSrc < zEnd && zSrc[0] == '0' ){
28341 		zSrc++;
28342 	}
28343 	zIn = zSrc;
28344 	for(;;){
28345 		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
28346 		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
28347 		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
28348 		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
28349 	}
28350 	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28351 		zSrc++;
28352 	}
28353 	if( zRest ){
28354 		*zRest = zSrc;
28355 	}
28356 	if( pOutVal ){
28357 		if( isNeg == TRUE && nVal != 0 ){
28358 			nVal = -nVal;
28359 		}
28360 		*(sxi64 *)pOutVal = nVal;
28361 	}
28362 	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
28363 }
SyOctalStrToInt64(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28364 JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28365 {
28366 	const char *zIn, *zEnd;
28367 	int isNeg = FALSE;
28368 	sxi64 nVal = 0;
28369 	int c;
28370 #if defined(UNTRUST)
28371 	if( SX_EMPTY_STR(zSrc) ){
28372 		if( pOutVal ){
28373 			*(sxi32 *)pOutVal = 0;
28374 		}
28375 		return SXERR_EMPTY;
28376 	}
28377 #endif
28378 	zEnd = &zSrc[nLen];
28379 	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28380 		zSrc++;
28381 	}
28382 	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28383 		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28384 		zSrc++;
28385 	}
28386 	/* Skip leading zero */
28387 	while(zSrc < zEnd && zSrc[0] == '0' ){
28388 		zSrc++;
28389 	}
28390 	zIn = zSrc;
28391 	for(;;){
28392 		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
28393 		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
28394 		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
28395 		if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
28396 	}
28397 	/* Skip trailing spaces */
28398 	while(zSrc < zEnd && SyisSpace(zSrc[0])){
28399 		zSrc++;
28400 	}
28401 	if( zRest ){
28402 		*zRest = zSrc;
28403 	}
28404 	if( pOutVal ){
28405 		if( isNeg == TRUE && nVal != 0 ){
28406 			nVal = -nVal;
28407 		}
28408 		*(sxi64 *)pOutVal = nVal;
28409 	}
28410 	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28411 }
SyBinaryStrToInt64(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28412 JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28413 {
28414 	const char *zIn, *zEnd;
28415 	int isNeg = FALSE;
28416 	sxi64 nVal = 0;
28417 	int c;
28418 #if defined(UNTRUST)
28419 	if( SX_EMPTY_STR(zSrc) ){
28420 		if( pOutVal ){
28421 			*(sxi32 *)pOutVal = 0;
28422 		}
28423 		return SXERR_EMPTY;
28424 	}
28425 #endif
28426 	zEnd = &zSrc[nLen];
28427 	while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
28428 		zSrc++;
28429 	}
28430 	if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
28431 		isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
28432 		zSrc++;
28433 	}
28434 	if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){
28435 		/* Bypass binary prefix */
28436 		zSrc += sizeof(char) * 2;
28437 	}
28438 	/* Skip leading zero */
28439 	while(zSrc < zEnd && zSrc[0] == '0' ){
28440 		zSrc++;
28441 	}
28442 	zIn = zSrc;
28443 	for(;;){
28444 		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28445 		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28446 		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28447 		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
28448 	}
28449 	/* Skip trailing spaces */
28450 	while(zSrc < zEnd && SyisSpace(zSrc[0])){
28451 		zSrc++;
28452 	}
28453 	if( zRest ){
28454 		*zRest = zSrc;
28455 	}
28456 	if( pOutVal ){
28457 		if( isNeg == TRUE && nVal != 0 ){
28458 			nVal = -nVal;
28459 		}
28460 		*(sxi64 *)pOutVal = nVal;
28461 	}
28462 	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
28463 }
SyStrToReal(const char * zSrc,sxu32 nLen,void * pOutVal,const char ** zRest)28464 JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
28465 {
28466 #define SXDBL_DIG        15
28467 #define SXDBL_MAX_EXP    308
28468 #define SXDBL_MIN_EXP_PLUS	307
28469 	static const sxreal aTab[] = {
28470 	10,
28471 	1.0e2,
28472 	1.0e4,
28473 	1.0e8,
28474 	1.0e16,
28475 	1.0e32,
28476 	1.0e64,
28477 	1.0e128,
28478 	1.0e256
28479 	};
28480 	sxu8 neg = FALSE;
28481 	sxreal Val = 0.0;
28482 	const char *zEnd;
28483 	sxi32 Lim, exp;
28484 	sxreal *p = 0;
28485 #ifdef UNTRUST
28486 	if( SX_EMPTY_STR(zSrc)  ){
28487 		if( pOutVal ){
28488 			*(sxreal *)pOutVal = 0.0;
28489 		}
28490 		return SXERR_EMPTY;
28491 	}
28492 #endif
28493 	zEnd = &zSrc[nLen];
28494 	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28495 		zSrc++;
28496 	}
28497 	if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){
28498 		neg =  zSrc[0] == '-' ? TRUE : FALSE ;
28499 		zSrc++;
28500 	}
28501 	Lim = SXDBL_DIG ;
28502 	for(;;){
28503 		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28504 		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28505 		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28506 		if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
28507 	}
28508 	if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){
28509 		sxreal dec = 1.0;
28510 		zSrc++;
28511 		for(;;){
28512 			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28513 			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28514 			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28515 			if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
28516 		}
28517 		Val /= dec;
28518 	}
28519 	if( neg == TRUE && Val != 0.0 ) {
28520 		Val = -Val ;
28521 	}
28522 	if( Lim <= 0 ){
28523 		/* jump overflow digit */
28524 		while( zSrc < zEnd ){
28525 			if( zSrc[0] == 'e' || zSrc[0] == 'E' ){
28526 				break;
28527 			}
28528 			zSrc++;
28529 		}
28530 	}
28531 	neg = FALSE;
28532 	if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){
28533 		zSrc++;
28534 		if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){
28535 			neg = zSrc[0] == '-' ? TRUE : FALSE ;
28536 			zSrc++;
28537 		}
28538 		exp = 0;
28539 		while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){
28540 			exp = exp * 10 + (zSrc[0] - '0');
28541 			zSrc++;
28542 		}
28543 		if( neg  ){
28544 			if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ;
28545 		}else if ( exp > SXDBL_MAX_EXP ){
28546 			exp = SXDBL_MAX_EXP;
28547 		}
28548 		for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){
28549 			if( exp & 01 ){
28550 				if( neg ){
28551 					Val /= *p ;
28552 				}else{
28553 					Val *= *p;
28554 				}
28555 			}
28556 		}
28557 	}
28558 	while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
28559 		zSrc++;
28560 	}
28561 	if( zRest ){
28562 		*zRest = zSrc;
28563 	}
28564 	if( pOutVal ){
28565 		*(sxreal *)pOutVal = Val;
28566 	}
28567 	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
28568 }
28569 /* SyRunTimeApi:sxlib.c  */
SyBinHash(const void * pSrc,sxu32 nLen)28570 JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen)
28571 {
28572 	register unsigned char *zIn = (unsigned char *)pSrc;
28573 	unsigned char *zEnd;
28574 	sxu32 nH = 5381;
28575 	zEnd = &zIn[nLen];
28576 	for(;;){
28577 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28578 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28579 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28580 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
28581 	}
28582 	return nH;
28583 }
28584 #ifndef JX9_DISABLE_BUILTIN_FUNC
SyBase64Encode(const char * zSrc,sxu32 nLen,ProcConsumer xConsumer,void * pUserData)28585 JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28586 {
28587 	static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28588 	unsigned char *zIn = (unsigned char *)zSrc;
28589 	unsigned char z64[4];
28590 	sxu32 i;
28591 	sxi32 rc;
28592 #if defined(UNTRUST)
28593 	if( SX_EMPTY_STR(zSrc) || xConsumer == 0){
28594 		return SXERR_EMPTY;
28595 	}
28596 #endif
28597 	for(i = 0; i + 2 < nLen; i += 3){
28598 		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28599 		z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F];
28600 		z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F];
28601 		z64[3] = zBase64[ zIn[i + 2] & 0x3F];
28602 
28603 		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28604 		if( rc != SXRET_OK ){return SXERR_ABORT;}
28605 
28606 	}
28607 	if ( i+1 < nLen ){
28608 		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28609 		z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F];
28610 		z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ];
28611 		z64[3] = '=';
28612 
28613 		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28614 		if( rc != SXRET_OK ){return SXERR_ABORT;}
28615 
28616 	}else if( i < nLen ){
28617 		z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
28618 		z64[1]   = zBase64[(zIn[i] & 0x03) << 4];
28619 		z64[2] = '=';
28620 		z64[3] = '=';
28621 
28622 		rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
28623 		if( rc != SXRET_OK ){return SXERR_ABORT;}
28624 	}
28625 
28626 	return SXRET_OK;
28627 }
SyBase64Decode(const char * zB64,sxu32 nLen,ProcConsumer xConsumer,void * pUserData)28628 JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28629 {
28630 	static const sxu32 aBase64Trans[] = {
28631 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28632 	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,
28633 	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,
28634 	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,
28635 	0, 0, 0
28636 	};
28637 	sxu32 n, w, x, y, z;
28638 	sxi32 rc;
28639 	unsigned char zOut[10];
28640 #if defined(UNTRUST)
28641 	if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){
28642 		return SXERR_EMPTY;
28643 	}
28644 #endif
28645 	while(nLen > 0 && zB64[nLen - 1] == '=' ){
28646 		nLen--;
28647 	}
28648 	for( n = 0 ; n+3<nLen ; n += 4){
28649 		w = aBase64Trans[zB64[n] & 0x7F];
28650 		x = aBase64Trans[zB64[n+1] & 0x7F];
28651 		y = aBase64Trans[zB64[n+2] & 0x7F];
28652 		z = aBase64Trans[zB64[n+3] & 0x7F];
28653 		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28654 		zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
28655 		zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F);
28656 
28657 		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData);
28658 		if( rc != SXRET_OK ){ return SXERR_ABORT;}
28659 	}
28660 	if( n+2 < nLen ){
28661 		w = aBase64Trans[zB64[n] & 0x7F];
28662 		x = aBase64Trans[zB64[n+1] & 0x7F];
28663 		y = aBase64Trans[zB64[n+2] & 0x7F];
28664 
28665 		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28666 		zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
28667 
28668 		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData);
28669 		if( rc != SXRET_OK ){ return SXERR_ABORT;}
28670 	}else if( n+1 < nLen ){
28671 		w = aBase64Trans[zB64[n] & 0x7F];
28672 		x = aBase64Trans[zB64[n+1] & 0x7F];
28673 
28674 		zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
28675 
28676 		rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData);
28677 		if( rc != SXRET_OK ){ return SXERR_ABORT;}
28678 	}
28679 	return SXRET_OK;
28680 }
28681 #endif /* JX9_DISABLE_BUILTIN_FUNC */
28682 #define INVALID_LEXER(LEX)	(  LEX == 0  || LEX->xTokenizer == 0 )
SyLexInit(SyLex * pLex,SySet * pSet,ProcTokenizer xTokenizer,void * pUserData)28683 JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData)
28684 {
28685 	SyStream *pStream;
28686 #if defined (UNTRUST)
28687 	if ( pLex == 0 || xTokenizer == 0 ){
28688 		return SXERR_CORRUPT;
28689 	}
28690 #endif
28691 	pLex->pTokenSet = 0;
28692 	/* Initialize lexer fields */
28693 	if( pSet ){
28694 		if ( SySetElemSize(pSet) != sizeof(SyToken) ){
28695 			return SXERR_INVALID;
28696 		}
28697 		pLex->pTokenSet = pSet;
28698 	}
28699 	pStream = &pLex->sStream;
28700 	pLex->xTokenizer = xTokenizer;
28701 	pLex->pUserData = pUserData;
28702 
28703 	pStream->nLine = 1;
28704 	pStream->nIgn  = 0;
28705 	pStream->zText = pStream->zEnd = 0;
28706 	pStream->pSet  = pSet;
28707 	return SXRET_OK;
28708 }
SyLexTokenizeInput(SyLex * pLex,const char * zInput,sxu32 nLen,void * pCtxData,ProcSort xSort,ProcCmp xCmp)28709 JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp)
28710 {
28711 	const unsigned char *zCur;
28712 	SyStream *pStream;
28713 	SyToken sToken;
28714 	sxi32 rc;
28715 #if defined (UNTRUST)
28716 	if ( INVALID_LEXER(pLex) || zInput == 0 ){
28717 		return SXERR_CORRUPT;
28718 	}
28719 #endif
28720 	pStream = &pLex->sStream;
28721 	/* Point to the head of the input */
28722 	pStream->zText = pStream->zInput = (const unsigned char *)zInput;
28723 	/* Point to the end of the input */
28724 	pStream->zEnd = &pStream->zInput[nLen];
28725 	for(;;){
28726 		if( pStream->zText >= pStream->zEnd ){
28727 			/* End of the input reached */
28728 			break;
28729 		}
28730 		zCur = pStream->zText;
28731 		/* Call the tokenizer callback */
28732 		rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData);
28733 		if( rc != SXRET_OK && rc != SXERR_CONTINUE ){
28734 			/* Tokenizer callback request an operation abort */
28735 			if( rc == SXERR_ABORT ){
28736 				return SXERR_ABORT;
28737 			}
28738 			break;
28739 		}
28740 		if( rc == SXERR_CONTINUE ){
28741 			/* Request to ignore this token */
28742 			pStream->nIgn++;
28743 		}else if( pLex->pTokenSet  ){
28744 			/* Put the token in the set */
28745 			rc = SySetPut(pLex->pTokenSet, (const void *)&sToken);
28746 			if( rc != SXRET_OK ){
28747 				break;
28748 			}
28749 		}
28750 		if( zCur >= pStream->zText ){
28751 			/* Automatic advance of the stream cursor */
28752 			pStream->zText = &zCur[1];
28753 		}
28754 	}
28755 	if( xSort &&  pLex->pTokenSet ){
28756 		SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet);
28757 		/* Sort the extrated tokens */
28758 		if( xCmp == 0 ){
28759 			/* Use a default comparison function */
28760 			xCmp = SyMemcmp;
28761 		}
28762 		xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp);
28763 	}
28764 	return SXRET_OK;
28765 }
SyLexRelease(SyLex * pLex)28766 JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex)
28767 {
28768 	sxi32 rc = SXRET_OK;
28769 #if defined (UNTRUST)
28770 	if ( INVALID_LEXER(pLex) ){
28771 		return SXERR_CORRUPT;
28772 	}
28773 #else
28774 	SXUNUSED(pLex); /* Prevent compiler warning */
28775 #endif
28776 	return rc;
28777 }
28778 #ifndef JX9_DISABLE_BUILTIN_FUNC
28779 #define SAFE_HTTP(C)	(SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' )
SyUriEncode(const char * zSrc,sxu32 nLen,ProcConsumer xConsumer,void * pUserData)28780 JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
28781 {
28782 	unsigned char *zIn = (unsigned char *)zSrc;
28783 	unsigned char zHex[3] = { '%', 0, 0 };
28784 	unsigned char zOut[2];
28785 	unsigned char *zCur, *zEnd;
28786 	sxi32 c;
28787 	sxi32 rc;
28788 #ifdef UNTRUST
28789 	if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
28790 		return SXERR_EMPTY;
28791 	}
28792 #endif
28793 	rc = SXRET_OK;
28794 	zEnd = &zIn[nLen]; zCur = zIn;
28795 	for(;;){
28796 		if( zCur >= zEnd ){
28797 			if( zCur != zIn ){
28798 				rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData);
28799 			}
28800 			break;
28801 		}
28802 		c = zCur[0];
28803 		if( SAFE_HTTP(c) ){
28804 			zCur++; continue;
28805 		}
28806 		if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){
28807 			break;
28808 		}
28809 		if( c == ' ' ){
28810 			zOut[0] = '+';
28811 			rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData);
28812 		}else{
28813 			zHex[1]	= "0123456789ABCDEF"[(c >> 4) & 0x0F];
28814 			zHex[2] = "0123456789ABCDEF"[c & 0x0F];
28815 			rc = xConsumer(zHex, sizeof(zHex), pUserData);
28816 		}
28817 		if( SXRET_OK != rc ){
28818 			break;
28819 		}
28820 		zIn = &zCur[1]; zCur = zIn ;
28821 	}
28822 	return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT;
28823 }
28824 #endif /* JX9_DISABLE_BUILTIN_FUNC */
SyAsciiToHex(sxi32 c)28825 static sxi32 SyAsciiToHex(sxi32 c)
28826 {
28827 	if( c >= 'a' && c <= 'f' ){
28828 		c += 10 - 'a';
28829 		return c;
28830 	}
28831 	if( c >= '0' && c <= '9' ){
28832 		c -= '0';
28833 		return c;
28834 	}
28835 	if( c >= 'A' && c <= 'F') {
28836 		c += 10 - 'A';
28837 		return c;
28838 	}
28839 	return 0;
28840 }
SyUriDecode(const char * zSrc,sxu32 nLen,ProcConsumer xConsumer,void * pUserData,int bUTF8)28841 JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8)
28842 {
28843 	static const sxu8 Utf8Trans[] = {
28844 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28845 		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
28846 		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
28847 		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
28848 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28849 		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
28850 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
28851 		0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
28852 	};
28853 	const char *zIn = zSrc;
28854 	const char *zEnd;
28855 	const char *zCur;
28856 	sxu8 *zOutPtr;
28857 	sxu8 zOut[10];
28858 	sxi32 c, d;
28859 	sxi32 rc;
28860 #if defined(UNTRUST)
28861 	if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
28862 		return SXERR_EMPTY;
28863 	}
28864 #endif
28865 	rc = SXRET_OK;
28866 	zEnd = &zSrc[nLen];
28867 	zCur = zIn;
28868 	for(;;){
28869 		while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){
28870 			zCur++;
28871 		}
28872 		if( zCur != zIn ){
28873 			/* Consume input */
28874 			rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData);
28875 			if( rc != SXRET_OK ){
28876 				/* User consumer routine request an operation abort */
28877 				break;
28878 			}
28879 		}
28880 		if( zCur >= zEnd ){
28881 			rc = SXRET_OK;
28882 			break;
28883 		}
28884 		/* Decode unsafe HTTP characters */
28885 		zOutPtr = zOut;
28886 		if( zCur[0] == '+' ){
28887 			*zOutPtr++ = ' ';
28888 			zCur++;
28889 		}else{
28890 			if( &zCur[2] >= zEnd ){
28891 				rc = SXERR_OVERFLOW;
28892 				break;
28893 			}
28894 			c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
28895 			zCur += 3;
28896 			if( c < 0x000C0 ){
28897 				*zOutPtr++ = (sxu8)c;
28898 			}else{
28899 				c = Utf8Trans[c-0xC0];
28900 				while( zCur[0] == '%' ){
28901 					d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
28902 					if( (d&0xC0) != 0x80 ){
28903 						break;
28904 					}
28905 					c = (c<<6) + (0x3f & d);
28906 					zCur += 3;
28907 				}
28908 				if( bUTF8 == FALSE ){
28909 					*zOutPtr++ = (sxu8)c;
28910 				}else{
28911 					SX_WRITE_UTF8(zOutPtr, c);
28912 				}
28913 			}
28914 
28915 		}
28916 		/* Consume the decoded characters */
28917 		rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData);
28918 		if( rc != SXRET_OK ){
28919 			break;
28920 		}
28921 		/* Synchronize pointers */
28922 		zIn = zCur;
28923 	}
28924 	return rc;
28925 }
28926 #ifndef JX9_DISABLE_BUILTIN_FUNC
28927 static const char *zEngDay[] = {
28928 	"Sunday", "Monday", "Tuesday", "Wednesday",
28929 	"Thursday", "Friday", "Saturday"
28930 };
28931 static const char *zEngMonth[] = {
28932 	"January", "February", "March", "April",
28933 	"May", "June", "July", "August",
28934 	"September", "October", "November", "December"
28935 };
GetDay(sxi32 i)28936 static const char * GetDay(sxi32 i)
28937 {
28938 	return zEngDay[ i % 7 ];
28939 }
GetMonth(sxi32 i)28940 static const char * GetMonth(sxi32 i)
28941 {
28942 	return zEngMonth[ i % 12 ];
28943 }
SyTimeGetDay(sxi32 iDay)28944 JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay)
28945 {
28946 	return GetDay(iDay);
28947 }
SyTimeGetMonth(sxi32 iMonth)28948 JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth)
28949 {
28950 	return GetMonth(iMonth);
28951 }
28952 #endif /* JX9_DISABLE_BUILTIN_FUNC */
28953 /* SyRunTimeApi: sxfmt.c */
28954 #define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
28955 /*
28956 ** Conversion types fall into various categories as defined by the
28957 ** following enumeration.
28958 */
28959 #define SXFMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
28960 #define SXFMT_FLOAT       2 /* Floating point.%f */
28961 #define SXFMT_EXP         3 /* Exponentional notation.%e and %E */
28962 #define SXFMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
28963 #define SXFMT_SIZE        5 /* Total number of characters processed so far.%n */
28964 #define SXFMT_STRING      6 /* Strings.%s */
28965 #define SXFMT_PERCENT     7 /* Percent symbol.%% */
28966 #define SXFMT_CHARX       8 /* Characters.%c */
28967 #define SXFMT_ERROR       9 /* Used to indicate no such conversion type */
28968 /* Extension by Symisc Systems */
28969 #define SXFMT_RAWSTR     13 /* %z Pointer to raw string (SyString *) */
28970 #define SXFMT_UNUSED     15
28971 /*
28972 ** Allowed values for SyFmtInfo.flags
28973 */
28974 #define SXFLAG_SIGNED	0x01
28975 #define SXFLAG_UNSIGNED 0x02
28976 /* Allowed values for SyFmtConsumer.nType */
28977 #define SXFMT_CONS_PROC		1	/* Consumer is a procedure */
28978 #define SXFMT_CONS_STR		2	/* Consumer is a managed string */
28979 #define SXFMT_CONS_FILE		5	/* Consumer is an open File */
28980 #define SXFMT_CONS_BLOB		6	/* Consumer is a BLOB */
28981 /*
28982 ** Each builtin conversion character (ex: the 'd' in "%d") is described
28983 ** by an instance of the following structure
28984 */
28985 typedef struct SyFmtInfo SyFmtInfo;
28986 struct SyFmtInfo
28987 {
28988   char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
28989   sxu8 base;     /* The base for radix conversion */
28990   int flags;    /* One or more of SXFLAG_ constants below */
28991   sxu8 type;     /* Conversion paradigm */
28992   char *charset; /* The character set for conversion */
28993   char *prefix;  /* Prefix on non-zero values in alt format */
28994 };
28995 typedef struct SyFmtConsumer SyFmtConsumer;
28996 struct SyFmtConsumer
28997 {
28998 	sxu32 nLen; /* Total output length */
28999 	sxi32 nType; /* Type of the consumer see below */
29000 	sxi32 rc;	/* Consumer return value;Abort processing if rc != SXRET_OK */
29001  union{
29002 	struct{
29003 	ProcConsumer xUserConsumer;
29004 	void *pUserData;
29005 	}sFunc;
29006 	SyBlob *pBlob;
29007  }uConsumer;
29008 };
29009 #ifndef SX_OMIT_FLOATINGPOINT
getdigit(sxlongreal * val,int * cnt)29010 static int getdigit(sxlongreal *val, int *cnt)
29011 {
29012   sxlongreal d;
29013   int digit;
29014 
29015   if( (*cnt)++ >= 16 ){
29016 	  return '0';
29017   }
29018   digit = (int)*val;
29019   d = digit;
29020    *val = (*val - d)*10.0;
29021   return digit + '0' ;
29022 }
29023 #endif /* SX_OMIT_FLOATINGPOINT */
29024 /*
29025  * The following routine was taken from the SQLITE2 source tree and was
29026  * extended by Symisc Systems to fit its need.
29027  * Status: Public Domain
29028  */
InternFormat(ProcConsumer xConsumer,void * pUserData,const char * zFormat,va_list ap)29029 static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap)
29030 {
29031 	/*
29032 	 * The following table is searched linearly, so it is good to put the most frequently
29033 	 * used conversion types first.
29034 	 */
29035 static const SyFmtInfo aFmt[] = {
29036   {  'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    },
29037   {  's',  0, 0, SXFMT_STRING,     0,                  0    },
29038   {  'c',  0, 0, SXFMT_CHARX,      0,                  0    },
29039   {  'x', 16, 0, SXFMT_RADIX,      "0123456789abcdef", "x0" },
29040   {  'X', 16, 0, SXFMT_RADIX,      "0123456789ABCDEF", "X0" },
29041          /* -- Extensions by Symisc Systems -- */
29042   {  'z',  0, 0, SXFMT_RAWSTR,     0,                   0   }, /* Pointer to a raw string (SyString *) */
29043   {  'B',  2, 0, SXFMT_RADIX,      "01",                "b0"},
29044          /* -- End of Extensions -- */
29045   {  'o',  8, 0, SXFMT_RADIX,      "01234567",         "0"  },
29046   {  'u', 10, 0, SXFMT_RADIX,      "0123456789",       0    },
29047 #ifndef SX_OMIT_FLOATINGPOINT
29048   {  'f',  0, SXFLAG_SIGNED, SXFMT_FLOAT,       0,     0    },
29049   {  'e',  0, SXFLAG_SIGNED, SXFMT_EXP,        "e",    0    },
29050   {  'E',  0, SXFLAG_SIGNED, SXFMT_EXP,        "E",    0    },
29051   {  'g',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "e",    0    },
29052   {  'G',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "E",    0    },
29053 #endif
29054   {  'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    },
29055   {  'n',  0, 0, SXFMT_SIZE,       0,                  0    },
29056   {  '%',  0, 0, SXFMT_PERCENT,    0,                  0    },
29057   {  'p', 10, 0, SXFMT_RADIX,      "0123456789",       0    }
29058 };
29059   int c;                     /* Next character in the format string */
29060   char *bufpt;               /* Pointer to the conversion buffer */
29061   int precision;             /* Precision of the current field */
29062   int length;                /* Length of the field */
29063   int idx;                   /* A general purpose loop counter */
29064   int width;                 /* Width of the current field */
29065   sxu8 flag_leftjustify;   /* True if "-" flag is present */
29066   sxu8 flag_plussign;      /* True if "+" flag is present */
29067   sxu8 flag_blanksign;     /* True if " " flag is present */
29068   sxu8 flag_alternateform; /* True if "#" flag is present */
29069   sxu8 flag_zeropad;       /* True if field width constant starts with zero */
29070   sxu8 flag_long;          /* True if "l" flag is present */
29071   sxi64 longvalue;         /* Value for integer types */
29072   const SyFmtInfo *infop;  /* Pointer to the appropriate info structure */
29073   char buf[SXFMT_BUFSIZ];  /* Conversion buffer */
29074   char prefix;             /* Prefix character."+" or "-" or " " or '\0'.*/
29075   sxu8 errorflag = 0;      /* True if an error is encountered */
29076   sxu8 xtype;              /* Conversion paradigm */
29077   char *zExtra;
29078   static char spaces[] = "                                                  ";
29079 #define etSPACESIZE ((int)sizeof(spaces)-1)
29080 #ifndef SX_OMIT_FLOATINGPOINT
29081   sxlongreal realvalue;    /* Value for real types */
29082   int  exp;                /* exponent of real numbers */
29083   double rounder;          /* Used for rounding floating point values */
29084   sxu8 flag_dp;            /* True if decimal point should be shown */
29085   sxu8 flag_rtz;           /* True if trailing zeros should be removed */
29086   sxu8 flag_exp;           /* True to force display of the exponent */
29087   int nsd;                 /* Number of significant digits returned */
29088 #endif
29089   int rc;
29090 
29091   length = 0;
29092   bufpt = 0;
29093   for(; (c=(*zFormat))!=0; ++zFormat){
29094     if( c!='%' ){
29095       unsigned int amt;
29096       bufpt = (char *)zFormat;
29097       amt = 1;
29098       while( (c=(*++zFormat))!='%' && c!=0 ) amt++;
29099 	  rc = xConsumer((const void *)bufpt, amt, pUserData);
29100 	  if( rc != SXRET_OK ){
29101 		  return SXERR_ABORT; /* Consumer routine request an operation abort */
29102 	  }
29103       if( c==0 ){
29104 		  return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
29105 	  }
29106     }
29107     if( (c=(*++zFormat))==0 ){
29108       errorflag = 1;
29109 	  rc = xConsumer("%", sizeof("%")-1, pUserData);
29110 	  if( rc != SXRET_OK ){
29111 		  return SXERR_ABORT; /* Consumer routine request an operation abort */
29112 	  }
29113       return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
29114     }
29115     /* Find out what flags are present */
29116     flag_leftjustify = flag_plussign = flag_blanksign =
29117      flag_alternateform = flag_zeropad = 0;
29118     do{
29119       switch( c ){
29120         case '-':   flag_leftjustify = 1;     c = 0;   break;
29121         case '+':   flag_plussign = 1;        c = 0;   break;
29122         case ' ':   flag_blanksign = 1;       c = 0;   break;
29123         case '#':   flag_alternateform = 1;   c = 0;   break;
29124         case '0':   flag_zeropad = 1;         c = 0;   break;
29125         default:                                       break;
29126       }
29127     }while( c==0 && (c=(*++zFormat))!=0 );
29128     /* Get the field width */
29129     width = 0;
29130     if( c=='*' ){
29131       width = va_arg(ap, int);
29132       if( width<0 ){
29133         flag_leftjustify = 1;
29134         width = -width;
29135       }
29136       c = *++zFormat;
29137     }else{
29138       while( c>='0' && c<='9' ){
29139         width = width*10 + c - '0';
29140         c = *++zFormat;
29141       }
29142     }
29143     if( width > SXFMT_BUFSIZ-10 ){
29144       width = SXFMT_BUFSIZ-10;
29145     }
29146     /* Get the precision */
29147 	precision = -1;
29148     if( c=='.' ){
29149       precision = 0;
29150       c = *++zFormat;
29151       if( c=='*' ){
29152         precision = va_arg(ap, int);
29153         if( precision<0 ) precision = -precision;
29154         c = *++zFormat;
29155       }else{
29156         while( c>='0' && c<='9' ){
29157           precision = precision*10 + c - '0';
29158           c = *++zFormat;
29159         }
29160       }
29161     }
29162     /* Get the conversion type modifier */
29163 	flag_long = 0;
29164     if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){
29165       flag_long = (c == 'q') ? 2 : 1;
29166       c = *++zFormat;
29167 	  if( c == 'l' ){
29168 		  /* Standard printf emulation 'lld' (expect a 64bit integer) */
29169 		  flag_long = 2;
29170 	  }
29171     }
29172     /* Fetch the info entry for the field */
29173     infop = 0;
29174     xtype = SXFMT_ERROR;
29175 	for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
29176       if( c==aFmt[idx].fmttype ){
29177         infop = &aFmt[idx];
29178 		xtype = infop->type;
29179         break;
29180       }
29181     }
29182     zExtra = 0;
29183 
29184     /*
29185     ** At this point, variables are initialized as follows:
29186     **
29187     **   flag_alternateform          TRUE if a '#' is present.
29188     **   flag_plussign               TRUE if a '+' is present.
29189     **   flag_leftjustify            TRUE if a '-' is present or if the
29190     **                               field width was negative.
29191     **   flag_zeropad                TRUE if the width began with 0.
29192     **   flag_long                   TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
29193     **                               the conversion character.
29194     **   flag_blanksign              TRUE if a ' ' is present.
29195     **   width                       The specified field width.This is
29196     **                               always non-negative.Zero is the default.
29197     **   precision                   The specified precision.The default
29198     **                               is -1.
29199     **   xtype                       The object of the conversion.
29200     **   infop                       Pointer to the appropriate info struct.
29201     */
29202     switch( xtype ){
29203       case SXFMT_RADIX:
29204         if( flag_long > 0 ){
29205 			if( flag_long > 1 ){
29206 				/* BSD quad: expect a 64-bit integer */
29207 				longvalue = va_arg(ap, sxi64);
29208 			}else{
29209 				longvalue = va_arg(ap, sxlong);
29210 			}
29211 		}else{
29212 			if( infop->flags & SXFLAG_SIGNED ){
29213 				longvalue = va_arg(ap, sxi32);
29214 			}else{
29215 				longvalue = va_arg(ap, sxu32);
29216 			}
29217 		}
29218 		/* Limit the precision to prevent overflowing buf[] during conversion */
29219       if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
29220 #if 1
29221         /* For the format %#x, the value zero is printed "0" not "0x0".
29222         ** I think this is stupid.*/
29223         if( longvalue==0 ) flag_alternateform = 0;
29224 #else
29225         /* More sensible: turn off the prefix for octal (to prevent "00"),
29226         ** but leave the prefix for hex.*/
29227         if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
29228 #endif
29229         if( infop->flags & SXFLAG_SIGNED ){
29230           if( longvalue<0 ){
29231             longvalue = -longvalue;
29232 			/* Ticket 1433-003 */
29233 			if( longvalue < 0 ){
29234 				/* Overflow */
29235 				longvalue= 0x7FFFFFFFFFFFFFFF;
29236 			}
29237             prefix = '-';
29238           }else if( flag_plussign )  prefix = '+';
29239           else if( flag_blanksign )  prefix = ' ';
29240           else                       prefix = 0;
29241         }else{
29242 			if( longvalue<0 ){
29243 				longvalue = -longvalue;
29244 				/* Ticket 1433-003 */
29245 				if( longvalue < 0 ){
29246 					/* Overflow */
29247 					longvalue= 0x7FFFFFFFFFFFFFFF;
29248 				}
29249 			}
29250 			prefix = 0;
29251 		}
29252         if( flag_zeropad && precision<width-(prefix!=0) ){
29253           precision = width-(prefix!=0);
29254         }
29255         bufpt = &buf[SXFMT_BUFSIZ-1];
29256         {
29257           register char *cset;      /* Use registers for speed */
29258           register int base;
29259           cset = infop->charset;
29260           base = infop->base;
29261           do{                                           /* Convert to ascii */
29262             *(--bufpt) = cset[longvalue%base];
29263             longvalue = longvalue/base;
29264           }while( longvalue>0 );
29265         }
29266         length = &buf[SXFMT_BUFSIZ-1]-bufpt;
29267         for(idx=precision-length; idx>0; idx--){
29268           *(--bufpt) = '0';                             /* Zero pad */
29269         }
29270         if( prefix ) *(--bufpt) = prefix;               /* Add sign */
29271         if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
29272           char *pre, x;
29273           pre = infop->prefix;
29274           if( *bufpt!=pre[0] ){
29275             for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
29276           }
29277         }
29278         length = &buf[SXFMT_BUFSIZ-1]-bufpt;
29279         break;
29280       case SXFMT_FLOAT:
29281       case SXFMT_EXP:
29282       case SXFMT_GENERIC:
29283 #ifndef SX_OMIT_FLOATINGPOINT
29284 		realvalue = va_arg(ap, double);
29285         if( precision<0 ) precision = 6;         /* Set default precision */
29286         if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40;
29287         if( realvalue<0.0 ){
29288           realvalue = -realvalue;
29289           prefix = '-';
29290         }else{
29291           if( flag_plussign )          prefix = '+';
29292           else if( flag_blanksign )    prefix = ' ';
29293           else                         prefix = 0;
29294         }
29295         if( infop->type==SXFMT_GENERIC && precision>0 ) precision--;
29296         rounder = 0.0;
29297 #if 0
29298         /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
29299         for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
29300 #else
29301         /* It makes more sense to use 0.5 */
29302         for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
29303 #endif
29304         if( infop->type==SXFMT_FLOAT ) realvalue += rounder;
29305         /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
29306         exp = 0;
29307         if( realvalue>0.0 ){
29308           while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
29309           while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
29310           while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
29311           while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
29312           if( exp>350 || exp<-350 ){
29313             bufpt = "NaN";
29314             length = 3;
29315             break;
29316           }
29317         }
29318         bufpt = buf;
29319         /*
29320         ** If the field type is etGENERIC, then convert to either etEXP
29321         ** or etFLOAT, as appropriate.
29322         */
29323         flag_exp = xtype==SXFMT_EXP;
29324         if( xtype!=SXFMT_FLOAT ){
29325           realvalue += rounder;
29326           if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
29327         }
29328         if( xtype==SXFMT_GENERIC ){
29329           flag_rtz = !flag_alternateform;
29330           if( exp<-4 || exp>precision ){
29331             xtype = SXFMT_EXP;
29332           }else{
29333             precision = precision - exp;
29334             xtype = SXFMT_FLOAT;
29335           }
29336         }else{
29337           flag_rtz = 0;
29338         }
29339         /*
29340         ** The "exp+precision" test causes output to be of type etEXP if
29341         ** the precision is too large to fit in buf[].
29342         */
29343         nsd = 0;
29344         if( xtype==SXFMT_FLOAT && exp+precision<SXFMT_BUFSIZ-30 ){
29345           flag_dp = (precision>0 || flag_alternateform);
29346           if( prefix ) *(bufpt++) = prefix;         /* Sign */
29347           if( exp<0 )  *(bufpt++) = '0';            /* Digits before "." */
29348           else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29349           if( flag_dp ) *(bufpt++) = '.';           /* The decimal point */
29350           for(exp++; exp<0 && precision>0; precision--, exp++){
29351             *(bufpt++) = '0';
29352           }
29353           while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29354           *(bufpt--) = 0;                           /* Null terminate */
29355           if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
29356             while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
29357             if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
29358           }
29359           bufpt++;                            /* point to next free slot */
29360         }else{    /* etEXP or etGENERIC */
29361           flag_dp = (precision>0 || flag_alternateform);
29362           if( prefix ) *(bufpt++) = prefix;   /* Sign */
29363           *(bufpt++) = (char)getdigit(&realvalue, &nsd);  /* First digit */
29364           if( flag_dp ) *(bufpt++) = '.';     /* Decimal point */
29365           while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
29366           bufpt--;                            /* point to last digit */
29367           if( flag_rtz && flag_dp ){          /* Remove tail zeros */
29368             while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
29369             if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
29370           }
29371           bufpt++;                            /* point to next free slot */
29372           if( exp || flag_exp ){
29373             *(bufpt++) = infop->charset[0];
29374             if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
29375             else       { *(bufpt++) = '+'; }
29376             if( exp>=100 ){
29377               *(bufpt++) = (char)((exp/100)+'0');                /* 100's digit */
29378               exp %= 100;
29379             }
29380             *(bufpt++) = (char)(exp/10+'0');                     /* 10's digit */
29381             *(bufpt++) = (char)(exp%10+'0');                     /* 1's digit */
29382           }
29383         }
29384         /* The converted number is in buf[] and zero terminated.Output it.
29385         ** Note that the number is in the usual order, not reversed as with
29386         ** integer conversions.*/
29387         length = bufpt-buf;
29388         bufpt = buf;
29389 
29390         /* Special case:  Add leading zeros if the flag_zeropad flag is
29391         ** set and we are not left justified */
29392         if( flag_zeropad && !flag_leftjustify && length < width){
29393           int i;
29394           int nPad = width - length;
29395           for(i=width; i>=nPad; i--){
29396             bufpt[i] = bufpt[i-nPad];
29397           }
29398           i = prefix!=0;
29399           while( nPad-- ) bufpt[i++] = '0';
29400           length = width;
29401         }
29402 #else
29403          bufpt = " ";
29404 		 length = (int)sizeof(" ") - 1;
29405 #endif /* SX_OMIT_FLOATINGPOINT */
29406         break;
29407       case SXFMT_SIZE:{
29408 		 int *pSize = va_arg(ap, int *);
29409 		 *pSize = ((SyFmtConsumer *)pUserData)->nLen;
29410 		 length = width = 0;
29411 					  }
29412         break;
29413       case SXFMT_PERCENT:
29414         buf[0] = '%';
29415         bufpt = buf;
29416         length = 1;
29417         break;
29418       case SXFMT_CHARX:
29419         c = va_arg(ap, int);
29420 		buf[0] = (char)c;
29421 		/* Limit the precision to prevent overflowing buf[] during conversion */
29422 		if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
29423         if( precision>=0 ){
29424           for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
29425           length = precision;
29426         }else{
29427           length =1;
29428         }
29429         bufpt = buf;
29430         break;
29431       case SXFMT_STRING:
29432         bufpt = va_arg(ap, char*);
29433         if( bufpt==0 ){
29434           bufpt = " ";
29435 		  length = (int)sizeof(" ")-1;
29436 		  break;
29437         }
29438 		length = precision;
29439 		if( precision < 0 ){
29440 			/* Symisc extension */
29441 			length = (int)SyStrlen(bufpt);
29442 		}
29443         if( precision>=0 && precision<length ) length = precision;
29444         break;
29445 	case SXFMT_RAWSTR:{
29446 		/* Symisc extension */
29447 		SyString *pStr = va_arg(ap, SyString *);
29448 		if( pStr == 0 || pStr->zString == 0 ){
29449 			 bufpt = " ";
29450 		     length = (int)sizeof(char);
29451 		     break;
29452 		}
29453 		bufpt = (char *)pStr->zString;
29454 		length = (int)pStr->nByte;
29455 		break;
29456 					  }
29457       case SXFMT_ERROR:
29458         buf[0] = '?';
29459         bufpt = buf;
29460 		length = (int)sizeof(char);
29461         if( c==0 ) zFormat--;
29462         break;
29463     }/* End switch over the format type */
29464     /*
29465     ** The text of the conversion is pointed to by "bufpt" and is
29466     ** "length" characters long.The field width is "width".Do
29467     ** the output.
29468     */
29469     if( !flag_leftjustify ){
29470       register int nspace;
29471       nspace = width-length;
29472       if( nspace>0 ){
29473         while( nspace>=etSPACESIZE ){
29474 			rc = xConsumer(spaces, etSPACESIZE, pUserData);
29475 			if( rc != SXRET_OK ){
29476 				return SXERR_ABORT; /* Consumer routine request an operation abort */
29477 			}
29478 			nspace -= etSPACESIZE;
29479         }
29480         if( nspace>0 ){
29481 			rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
29482 			if( rc != SXRET_OK ){
29483 				return SXERR_ABORT; /* Consumer routine request an operation abort */
29484 			}
29485 		}
29486       }
29487     }
29488     if( length>0 ){
29489 		rc = xConsumer(bufpt, (unsigned int)length, pUserData);
29490 		if( rc != SXRET_OK ){
29491 		  return SXERR_ABORT; /* Consumer routine request an operation abort */
29492 		}
29493     }
29494     if( flag_leftjustify ){
29495       register int nspace;
29496       nspace = width-length;
29497       if( nspace>0 ){
29498         while( nspace>=etSPACESIZE ){
29499 			rc = xConsumer(spaces, etSPACESIZE, pUserData);
29500 			if( rc != SXRET_OK ){
29501 				return SXERR_ABORT; /* Consumer routine request an operation abort */
29502 			}
29503 			nspace -= etSPACESIZE;
29504         }
29505         if( nspace>0 ){
29506 			rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
29507 			if( rc != SXRET_OK ){
29508 				return SXERR_ABORT; /* Consumer routine request an operation abort */
29509 			}
29510 		}
29511       }
29512     }
29513   }/* End for loop over the format string */
29514   return errorflag ? SXERR_FORMAT : SXRET_OK;
29515 }
FormatConsumer(const void * pSrc,unsigned int nLen,void * pData)29516 static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData)
29517 {
29518 	SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
29519 	sxi32 rc = SXERR_ABORT;
29520 	switch(pConsumer->nType){
29521 	case SXFMT_CONS_PROC:
29522 			/* User callback */
29523 			rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
29524 			break;
29525 	case SXFMT_CONS_BLOB:
29526 			/* Blob consumer */
29527 			rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
29528 			break;
29529 		default:
29530 			/* Unknown consumer */
29531 			break;
29532 	}
29533 	/* Update total number of bytes consumed so far */
29534 	pConsumer->nLen += nLen;
29535 	pConsumer->rc = rc;
29536 	return rc;
29537 }
FormatMount(sxi32 nType,void * pConsumer,ProcConsumer xUserCons,void * pUserData,sxu32 * pOutLen,const char * zFormat,va_list ap)29538 static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap)
29539 {
29540 	SyFmtConsumer sCons;
29541 	sCons.nType = nType;
29542 	sCons.rc = SXRET_OK;
29543 	sCons.nLen = 0;
29544 	if( pOutLen ){
29545 		*pOutLen = 0;
29546 	}
29547 	switch(nType){
29548 	case SXFMT_CONS_PROC:
29549 #if defined(UNTRUST)
29550 			if( xUserCons == 0 ){
29551 				return SXERR_EMPTY;
29552 			}
29553 #endif
29554 			sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
29555 			sCons.uConsumer.sFunc.pUserData	    = pUserData;
29556 		break;
29557 		case SXFMT_CONS_BLOB:
29558 			sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
29559 			break;
29560 		default:
29561 			return SXERR_UNKNOWN;
29562 	}
29563 	InternFormat(FormatConsumer, &sCons, zFormat, ap);
29564 	if( pOutLen ){
29565 		*pOutLen = sCons.nLen;
29566 	}
29567 	return sCons.rc;
29568 }
SyProcFormat(ProcConsumer xConsumer,void * pData,const char * zFormat,...)29569 JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...)
29570 {
29571 	va_list ap;
29572 	sxi32 rc;
29573 #if defined(UNTRUST)
29574 	if( SX_EMPTY_STR(zFormat) ){
29575 		return SXERR_EMPTY;
29576 	}
29577 #endif
29578 	va_start(ap, zFormat);
29579 	rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
29580 	va_end(ap);
29581 	return rc;
29582 }
SyBlobFormat(SyBlob * pBlob,const char * zFormat,...)29583 JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...)
29584 {
29585 	va_list ap;
29586 	sxu32 n;
29587 #if defined(UNTRUST)
29588 	if( SX_EMPTY_STR(zFormat) ){
29589 		return 0;
29590 	}
29591 #endif
29592 	va_start(ap, zFormat);
29593 	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
29594 	va_end(ap);
29595 	return n;
29596 }
SyBlobFormatAp(SyBlob * pBlob,const char * zFormat,va_list ap)29597 JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap)
29598 {
29599 	sxu32 n = 0; /* cc warning */
29600 #if defined(UNTRUST)
29601 	if( SX_EMPTY_STR(zFormat) ){
29602 		return 0;
29603 	}
29604 #endif
29605 	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
29606 	return n;
29607 }
SyBufferFormat(char * zBuf,sxu32 nLen,const char * zFormat,...)29608 JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...)
29609 {
29610 	SyBlob sBlob;
29611 	va_list ap;
29612 	sxu32 n;
29613 #if defined(UNTRUST)
29614 	if( SX_EMPTY_STR(zFormat) ){
29615 		return 0;
29616 	}
29617 #endif
29618 	if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){
29619 		return 0;
29620 	}
29621 	va_start(ap, zFormat);
29622 	FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
29623 	va_end(ap);
29624 	n = SyBlobLength(&sBlob);
29625 	/* Append the null terminator */
29626 	sBlob.mByte++;
29627 	SyBlobAppend(&sBlob, "\0", sizeof(char));
29628 	return n;
29629 }
29630 #ifndef JX9_DISABLE_BUILTIN_FUNC
29631 /*
29632  * Zip File Format:
29633  *
29634  * Byte order: Little-endian
29635  *
29636  * [Local file header + Compressed data [+ Extended local header]?]*
29637  * [Central directory]*
29638  * [End of central directory record]
29639  *
29640  * Local file header:*
29641  * Offset   Length   Contents
29642  *  0      4 bytes  Local file header signature (0x04034b50)
29643  *  4      2 bytes  Version needed to extract
29644  *  6      2 bytes  General purpose bit flag
29645  *  8      2 bytes  Compression method
29646  * 10      2 bytes  Last mod file time
29647  * 12      2 bytes  Last mod file date
29648  * 14      4 bytes  CRC-32
29649  * 18      4 bytes  Compressed size (n)
29650  * 22      4 bytes  Uncompressed size
29651  * 26      2 bytes  Filename length (f)
29652  * 28      2 bytes  Extra field length (e)
29653  * 30     (f)bytes  Filename
29654  *        (e)bytes  Extra field
29655  *        (n)bytes  Compressed data
29656  *
29657  * Extended local header:*
29658  * Offset   Length   Contents
29659  *  0      4 bytes  Extended Local file header signature (0x08074b50)
29660  *  4      4 bytes  CRC-32
29661  *  8      4 bytes  Compressed size
29662  * 12      4 bytes  Uncompressed size
29663  *
29664  * Extra field:?(if any)
29665  * Offset 	Length		Contents
29666  * 0	  	2 bytes		Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip
29667  * 2	  	2 bytes		Data size (g)
29668  * 		  	(g) bytes	(g) bytes of extra field
29669  *
29670  * Central directory:*
29671  * Offset   Length   Contents
29672  *  0      4 bytes  Central file header signature (0x02014b50)
29673  *  4      2 bytes  Version made by
29674  *  6      2 bytes  Version needed to extract
29675  *  8      2 bytes  General purpose bit flag
29676  * 10      2 bytes  Compression method
29677  * 12      2 bytes  Last mod file time
29678  * 14      2 bytes  Last mod file date
29679  * 16      4 bytes  CRC-32
29680  * 20      4 bytes  Compressed size
29681  * 24      4 bytes  Uncompressed size
29682  * 28      2 bytes  Filename length (f)
29683  * 30      2 bytes  Extra field length (e)
29684  * 32      2 bytes  File comment length (c)
29685  * 34      2 bytes  Disk number start
29686  * 36      2 bytes  Internal file attributes
29687  * 38      4 bytes  External file attributes
29688  * 42      4 bytes  Relative offset of local header
29689  * 46     (f)bytes  Filename
29690  *        (e)bytes  Extra field
29691  *        (c)bytes  File comment
29692  *
29693  * End of central directory record:
29694  * Offset   Length   Contents
29695  *  0      4 bytes  End of central dir signature (0x06054b50)
29696  *  4      2 bytes  Number of this disk
29697  *  6      2 bytes  Number of the disk with the start of the central directory
29698  *  8      2 bytes  Total number of entries in the central dir on this disk
29699  * 10      2 bytes  Total number of entries in the central dir
29700  * 12      4 bytes  Size of the central directory
29701  * 16      4 bytes  Offset of start of central directory with respect to the starting disk number
29702  * 20      2 bytes  zipfile comment length (c)
29703  * 22     (c)bytes  zipfile comment
29704  *
29705  * compression method: (2 bytes)
29706  *          0 - The file is stored (no compression)
29707  *          1 - The file is Shrunk
29708  *          2 - The file is Reduced with compression factor 1
29709  *          3 - The file is Reduced with compression factor 2
29710  *          4 - The file is Reduced with compression factor 3
29711  *          5 - The file is Reduced with compression factor 4
29712  *          6 - The file is Imploded
29713  *          7 - Reserved for Tokenizing compression algorithm
29714  *          8 - The file is Deflated
29715  */
29716 
29717 #define SXMAKE_ZIP_WORKBUF	(SXU16_HIGH/2)	/* 32KB Initial working buffer size */
29718 #define SXMAKE_ZIP_EXTRACT_VER	0x000a	/* Version needed to extract */
29719 #define SXMAKE_ZIP_VER	0x003	/* Version made by */
29720 
29721 #define SXZIP_CENTRAL_MAGIC			0x02014b50
29722 #define SXZIP_END_CENTRAL_MAGIC		0x06054b50
29723 #define SXZIP_LOCAL_MAGIC			0x04034b50
29724 /*#define SXZIP_CRC32_START			0xdebb20e3*/
29725 
29726 #define SXZIP_LOCAL_HDRSZ		30	/* Local header size */
29727 #define SXZIP_LOCAL_EXT_HDRZ	16	/* Extended local header(footer) size */
29728 #define SXZIP_CENTRAL_HDRSZ		46	/* Central directory header size */
29729 #define SXZIP_END_CENTRAL_HDRSZ	22	/* End of central directory header size */
29730 
29731 #define SXARCHIVE_HASH_SIZE	64 /* Starting hash table size(MUST BE POWER OF 2)*/
SyLittleEndianUnpack32(sxu32 * uNB,const unsigned char * buf,sxu32 Len)29732 static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len)
29733 {
29734 	if( Len < sizeof(sxu32) ){
29735 		return SXERR_SHORT;
29736 	}
29737 	*uNB =  buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
29738 	return SXRET_OK;
29739 }
SyLittleEndianUnpack16(sxu16 * pOut,const unsigned char * zBuf,sxu32 nLen)29740 static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen)
29741 {
29742 	if( nLen < sizeof(sxu16) ){
29743 		return SXERR_SHORT;
29744 	}
29745 	*pOut = zBuf[0] + (zBuf[1] <<8);
29746 
29747 	return SXRET_OK;
29748 }
29749 /*
29750  * Archive hashtable manager
29751  */
ArchiveHashGetEntry(SyArchive * pArch,const char * zName,sxu32 nLen,SyArchiveEntry ** ppEntry)29752 static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry)
29753 {
29754 	SyArchiveEntry *pBucketEntry;
29755 	SyString sEntry;
29756 	sxu32 nHash;
29757 
29758 	nHash = pArch->xHash(zName, nLen);
29759 	pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)];
29760 
29761 	SyStringInitFromBuf(&sEntry, zName, nLen);
29762 
29763 	for(;;){
29764 		if( pBucketEntry == 0 ){
29765 			break;
29766 		}
29767 		if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){
29768 			if( ppEntry ){
29769 				*ppEntry = pBucketEntry;
29770 			}
29771 			return SXRET_OK;
29772 		}
29773 		pBucketEntry = pBucketEntry->pNextHash;
29774 	}
29775 	return SXERR_NOTFOUND;
29776 }
ArchiveHashBucketInstall(SyArchiveEntry ** apTable,sxu32 nBucket,SyArchiveEntry * pEntry)29777 static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry)
29778 {
29779 	pEntry->pNextHash = apTable[nBucket];
29780 	if( apTable[nBucket] != 0 ){
29781 		apTable[nBucket]->pPrevHash = pEntry;
29782 	}
29783 	apTable[nBucket] = pEntry;
29784 }
ArchiveHashGrowTable(SyArchive * pArch)29785 static sxi32 ArchiveHashGrowTable(SyArchive *pArch)
29786 {
29787 	sxu32 nNewSize = pArch->nSize * 2;
29788 	SyArchiveEntry **apNew;
29789 	SyArchiveEntry *pEntry;
29790 	sxu32 n;
29791 
29792 	/* Allocate a new table */
29793 	apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *));
29794 	if( apNew == 0 ){
29795 		return SXRET_OK; /* Not so fatal, simply a performance hit */
29796 	}
29797 	SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *));
29798 	/* Rehash old entries */
29799 	for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){
29800 		pEntry->pNextHash = pEntry->pPrevHash = 0;
29801 		ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry);
29802 	}
29803 	/* Release the old table */
29804 	SyMemBackendFree(pArch->pAllocator, pArch->apHash);
29805 	pArch->apHash = apNew;
29806 	pArch->nSize = nNewSize;
29807 
29808 	return SXRET_OK;
29809 }
ArchiveHashInstallEntry(SyArchive * pArch,SyArchiveEntry * pEntry)29810 static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry)
29811 {
29812 	if( pArch->nLoaded > pArch->nSize * 3 ){
29813 		ArchiveHashGrowTable(&(*pArch));
29814 	}
29815 	pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName));
29816 	/* Install the entry in its bucket */
29817 	ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry);
29818 	MACRO_LD_PUSH(pArch->pList, pEntry);
29819 	pArch->nLoaded++;
29820 
29821 	return SXRET_OK;
29822 }
29823  /*
29824   * Parse the End of central directory and report status
29825   */
ParseEndOfCentralDirectory(SyArchive * pArch,const unsigned char * zBuf)29826  static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf)
29827  {
29828 	sxu32 nMagic = 0; /* cc -O6 warning */
29829  	sxi32 rc;
29830 
29831  	/* Sanity check */
29832  	rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32));
29833  	if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){
29834  		return SXERR_CORRUPT;
29835  	}
29836  	/* # of entries */
29837  	rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16));
29838  	if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){
29839  		return SXERR_CORRUPT;
29840  	}
29841  	/* Size of central directory */
29842  	rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32));
29843  	if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
29844  		return SXERR_CORRUPT;
29845  	}
29846  	/* Starting offset of central directory */
29847  	rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32));
29848  	if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
29849  		return SXERR_CORRUPT;
29850  	}
29851 
29852  	return SXRET_OK;
29853  }
29854  /*
29855   * Fill the zip entry with the appropriate information from the central directory
29856   */
GetCentralDirectoryEntry(SyArchive * pArch,SyArchiveEntry * pEntry,const unsigned char * zCentral,sxu32 * pNextOffset)29857 static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset)
29858  {
29859  	SyString *pName = &pEntry->sFileName; /* File name */
29860  	sxu16 nDosDate, nDosTime;
29861 	sxu16 nComment = 0 ;
29862 	sxu32 nMagic = 0; /* cc -O6 warning */
29863 	sxi32 rc;
29864 	nDosDate = nDosTime = 0; /* cc -O6 warning */
29865 	SXUNUSED(pArch);
29866  	/* Sanity check */
29867  	rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32));
29868  	if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){
29869  		rc = SXERR_CORRUPT;
29870  		/*
29871  		 * Try to recover by examing the next central directory record.
29872  		 * Dont worry here, there is no risk of an infinite loop since
29873 		 * the buffer size is delimited.
29874  		 */
29875 
29876  		/* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */
29877  		goto update;
29878  	}
29879  	/*
29880  	 * entry name length
29881  	 */
29882  	SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16));
29883  	if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){
29884  		 rc = SXERR_BIG;
29885  		 goto update;
29886  	}
29887  	/* Extra information */
29888  	SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16));
29889  	/* Comment length  */
29890  	SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16));
29891  	/* Compression method 0 == stored / 8 == deflated */
29892  	rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16));
29893  	/* DOS Timestamp */
29894  	SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16));
29895  	SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16));
29896  	SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt);
29897 	/* Little hack to fix month index  */
29898 	pEntry->sFmt.tm_mon--;
29899  	/* CRC32 */
29900  	rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32));
29901  	/* Content size before compression */
29902  	rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32));
29903  	if(  pEntry->nByte > SXI32_HIGH ){
29904  		rc = SXERR_BIG;
29905  		goto update;
29906  	}
29907  	/*
29908  	 * Content size after compression.
29909  	 * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr
29910  	 */
29911  	rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32));
29912  	if( pEntry->nByteCompr > SXI32_HIGH ){
29913  		rc = SXERR_BIG;
29914  		goto update;
29915  	}
29916  	/* Finally grab the contents offset */
29917  	SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32));
29918  	if( pEntry->nOfft > SXI32_HIGH ){
29919  		rc = SXERR_BIG;
29920  		goto update;
29921  	}
29922   	 rc = SXRET_OK;
29923 update:
29924  	/* Update the offset to point to the next central directory record */
29925  	*pNextOffset =  SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment;
29926  	return rc; /* Report failure or success */
29927 }
ZipFixOffset(SyArchiveEntry * pEntry,void * pSrc)29928 static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc)
29929 {
29930 	sxu16 nExtra, nNameLen;
29931 	unsigned char *zHdr;
29932 	nExtra = nNameLen = 0;
29933 	zHdr = (unsigned char *)pSrc;
29934 	zHdr = &zHdr[pEntry->nOfft];
29935 	if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){
29936 		return SXERR_CORRUPT;
29937 	}
29938 	SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16));
29939 	SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16));
29940 	/* Fix contents offset */
29941 	pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen;
29942 	return SXRET_OK;
29943 }
29944 /*
29945  * Extract all valid entries from the central directory
29946  */
ZipExtract(SyArchive * pArch,const unsigned char * zCentral,sxu32 nLen,void * pSrc)29947 static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc)
29948 {
29949 	SyArchiveEntry *pEntry, *pDup;
29950 	const unsigned char *zEnd ; /* End of central directory */
29951 	sxu32 nIncr, nOfft;          /* Central Offset */
29952 	SyString *pName;	        /* Entry name */
29953 	char *zName;
29954 	sxi32 rc;
29955 
29956 	nOfft = nIncr = 0;
29957 	zEnd = &zCentral[nLen];
29958 
29959 	for(;;){
29960 		if( &zCentral[nOfft] >= zEnd ){
29961 			break;
29962 		}
29963 		/* Add a new entry */
29964 		pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry));
29965 		if( pEntry == 0 ){
29966 			break;
29967 		}
29968 		SyZero(pEntry, sizeof(SyArchiveEntry));
29969 		pEntry->nMagic = SXARCH_MAGIC;
29970 		nIncr = 0;
29971 		rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr);
29972 		if( rc == SXRET_OK ){
29973 			/* Fix the starting record offset so we can access entry contents correctly */
29974 			rc = ZipFixOffset(pEntry, pSrc);
29975 		}
29976 		if(rc != SXRET_OK ){
29977 			sxu32 nJmp = 0;
29978 			SyMemBackendPoolFree(pArch->pAllocator, pEntry);
29979 			/* Try to recover by brute-forcing for a valid central directory record */
29980 			if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]),
29981 				(const void *)"PK\001\002", sizeof(sxu32), &nJmp)){
29982 					nOfft += nIncr + nJmp; /* Check next entry */
29983 					continue;
29984 			}
29985 			break; /* Giving up, archive is hopelessly corrupted */
29986 		}
29987 		pName = &pEntry->sFileName;
29988 		pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ];
29989 		if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){
29990 			/* Ignore zero length records (except folders) and records without names */
29991 			SyMemBackendPoolFree(pArch->pAllocator, pEntry);
29992 		 	nOfft += nIncr; /* Check next entry */
29993 			continue;
29994 		}
29995 		zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte);
29996  	 	if( zName == 0 ){
29997  	 		 SyMemBackendPoolFree(pArch->pAllocator, pEntry);
29998 		 	 nOfft += nIncr; /* Check next entry */
29999 			continue;
30000  	 	}
30001 		pName->zString = (const char *)zName;
30002 		/* Check for duplicates */
30003 		rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup);
30004 		if( rc == SXRET_OK ){
30005 			/* Another entry with the same name exists ; link them together */
30006 			pEntry->pNextName = pDup->pNextName;
30007 			pDup->pNextName = pEntry;
30008 			pDup->nDup++;
30009 		}else{
30010 			/* Insert in hashtable */
30011 			ArchiveHashInstallEntry(pArch, pEntry);
30012 		}
30013 		nOfft += nIncr;	/* Check next record */
30014 	}
30015 	pArch->pCursor = pArch->pList;
30016 
30017 	return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY;
30018 }
SyZipExtractFromBuf(SyArchive * pArch,const char * zBuf,sxu32 nLen)30019 JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen)
30020  {
30021  	const unsigned char *zCentral, *zEnd;
30022  	sxi32 rc;
30023 #if defined(UNTRUST)
30024  	if( SXARCH_INVALID(pArch) || zBuf == 0 ){
30025  		return SXERR_INVALID;
30026  	}
30027 #endif
30028  	/* The miminal size of a zip archive:
30029  	 * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ
30030  	 * 		30				46				22
30031  	 */
30032  	 if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){
30033  	 	return SXERR_CORRUPT; /* Don't bother processing return immediately */
30034  	 }
30035 
30036  	zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ];
30037  	/* Find the end of central directory */
30038  	while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) &&
30039 		zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){
30040  		zEnd--;
30041  	}
30042  	/* Parse the end of central directory */
30043  	rc = ParseEndOfCentralDirectory(&(*pArch), zEnd);
30044  	if( rc != SXRET_OK ){
30045  		return rc;
30046  	}
30047 
30048  	/* Find the starting offset of the central directory */
30049  	zCentral = &zEnd[-(sxi32)pArch->nCentralSize];
30050  	if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
30051  		if( pArch->nCentralOfft >= nLen ){
30052 			/* Corrupted central directory offset */
30053  			return SXERR_CORRUPT;
30054  		}
30055  		zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft];
30056  		if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
30057  			/* Corrupted zip archive */
30058  			return SXERR_CORRUPT;
30059  		}
30060  		/* Fall thru and extract all valid entries from the central directory */
30061  	}
30062  	rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf);
30063  	return rc;
30064  }
30065 /*
30066   * Default comparison function.
30067   */
ArchiveHashCmp(const SyString * pStr1,const SyString * pStr2)30068  static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2)
30069  {
30070 	 sxi32 rc;
30071 	 rc = SyStringCmp(pStr1, pStr2, SyMemcmp);
30072 	 return rc;
30073  }
SyArchiveInit(SyArchive * pArch,SyMemBackend * pAllocator,ProcHash xHash,ProcRawStrCmp xCmp)30074 JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp)
30075  {
30076 	SyArchiveEntry **apHash;
30077 #if defined(UNTRUST)
30078  	if( pArch == 0 ){
30079  		return SXERR_EMPTY;
30080  	}
30081 #endif
30082  	SyZero(pArch, sizeof(SyArchive));
30083  	/* Allocate a new hashtable */
30084 	apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
30085 	if( apHash == 0){
30086 		return SXERR_MEM;
30087 	}
30088 	SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
30089 	pArch->apHash = apHash;
30090 	pArch->xHash  = xHash ? xHash : SyBinHash;
30091 	pArch->xCmp   = xCmp ? xCmp : ArchiveHashCmp;
30092 	pArch->nSize  = SXARCHIVE_HASH_SIZE;
30093  	pArch->pAllocator = &(*pAllocator);
30094  	pArch->nMagic = SXARCH_MAGIC;
30095  	return SXRET_OK;
30096  }
ArchiveReleaseEntry(SyMemBackend * pAllocator,SyArchiveEntry * pEntry)30097  static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry)
30098  {
30099  	SyArchiveEntry *pDup = pEntry->pNextName;
30100  	SyArchiveEntry *pNextDup;
30101 
30102  	/* Release duplicates first since there are not stored in the hashtable */
30103  	for(;;){
30104  		if( pEntry->nDup == 0 ){
30105  			break;
30106  		}
30107  		pNextDup = pDup->pNextName;
30108 		pDup->nMagic = 0x2661;
30109  		SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName));
30110  		SyMemBackendPoolFree(pAllocator, pDup);
30111  		pDup = pNextDup;
30112  		pEntry->nDup--;
30113  	}
30114 	pEntry->nMagic = 0x2661;
30115   	SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName));
30116  	SyMemBackendPoolFree(pAllocator, pEntry);
30117  	return SXRET_OK;
30118  }
SyArchiveRelease(SyArchive * pArch)30119 JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch)
30120  {
30121 	SyArchiveEntry *pEntry, *pNext;
30122  	pEntry = pArch->pList;
30123 	for(;;){
30124 		if( pArch->nLoaded < 1 ){
30125 			break;
30126 		}
30127 		pNext = pEntry->pNext;
30128 		MACRO_LD_REMOVE(pArch->pList, pEntry);
30129 		ArchiveReleaseEntry(pArch->pAllocator, pEntry);
30130 		pEntry = pNext;
30131 		pArch->nLoaded--;
30132 	}
30133 	SyMemBackendFree(pArch->pAllocator, pArch->apHash);
30134 	pArch->pCursor = 0;
30135 	pArch->nMagic = 0x2626;
30136 	return SXRET_OK;
30137  }
SyArchiveResetLoopCursor(SyArchive * pArch)30138  JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch)
30139  {
30140 	pArch->pCursor = pArch->pList;
30141 	return SXRET_OK;
30142  }
SyArchiveGetNextEntry(SyArchive * pArch,SyArchiveEntry ** ppEntry)30143  JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry)
30144  {
30145 	SyArchiveEntry *pNext;
30146 	if( pArch->pCursor == 0 ){
30147 		/* Rewind the cursor */
30148 		pArch->pCursor = pArch->pList;
30149 		return SXERR_EOF;
30150 	}
30151 	*ppEntry = pArch->pCursor;
30152 	 pNext = pArch->pCursor->pNext;
30153 	 /* Advance the cursor to the next entry */
30154 	 pArch->pCursor = pNext;
30155 	 return SXRET_OK;
30156   }
30157 #endif /* JX9_DISABLE_BUILTIN_FUNC */
30158 /*
30159  * Psuedo Random Number Generator (PRNG)
30160  * @authors: SQLite authors <http://www.sqlite.org/>
30161  * @status: Public Domain
30162  * NOTE:
30163  *  Nothing in this file or anywhere else in the library does any kind of
30164  *  encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
30165  *  number generator) not as an encryption device.
30166  */
30167 #define SXPRNG_MAGIC	0x13C4
30168 #ifdef __UNIXES__
30169 #include <sys/types.h>
30170 #include <sys/stat.h>
30171 #include <fcntl.h>
30172 #include <unistd.h>
30173 #include <errno.h>
30174 #include <time.h>
30175 #include <sys/time.h>
30176 #endif
SyOSUtilRandomSeed(void * pBuf,sxu32 nLen,void * pUnused)30177 static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused)
30178 {
30179 	char *zBuf = (char *)pBuf;
30180 #ifdef __WINNT__
30181 	DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */
30182 #elif defined(__UNIXES__)
30183 	pid_t pid;
30184 	int fd;
30185 #else
30186 	char zGarbage[128]; /* Yes, keep this buffer uninitialized */
30187 #endif
30188 	SXUNUSED(pUnused);
30189 #ifdef __WINNT__
30190 #ifndef __MINGW32__
30191 	nProcessID = GetProcessId(GetCurrentProcess());
30192 #endif
30193 	SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
30194 	if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME)  ){
30195 		GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
30196 	}
30197 #elif defined(__UNIXES__)
30198 	fd = open("/dev/urandom", O_RDONLY);
30199 	if (fd >= 0 ){
30200 		if( read(fd, zBuf, nLen) > 0 ){
30201 			close(fd);
30202 			return SXRET_OK;
30203 		}
30204 		close(fd);
30205 		/* FALL THRU */
30206 	}
30207 	pid = getpid();
30208 	SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
30209 	if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval)  ){
30210 		gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
30211 	}
30212 #else
30213 	/* Fill with uninitialized data */
30214 	SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
30215 #endif
30216 	return SXRET_OK;
30217 }
SyRandomnessInit(SyPRNGCtx * pCtx,ProcRandomSeed xSeed,void * pUserData)30218 JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData)
30219 {
30220 	char zSeed[256];
30221 	sxu8 t;
30222 	sxi32 rc;
30223 	sxu32 i;
30224 	if( pCtx->nMagic == SXPRNG_MAGIC ){
30225 		return SXRET_OK; /* Already initialized */
30226 	}
30227  /* Initialize the state of the random number generator once,
30228   ** the first time this routine is called.The seed value does
30229   ** not need to contain a lot of randomness since we are not
30230   ** trying to do secure encryption or anything like that...
30231   */
30232 	if( xSeed == 0 ){
30233 		xSeed = SyOSUtilRandomSeed;
30234 	}
30235 	rc = xSeed(zSeed, sizeof(zSeed), pUserData);
30236 	if( rc != SXRET_OK ){
30237 		return rc;
30238 	}
30239 	pCtx->i = pCtx->j = 0;
30240 	for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){
30241 		pCtx->s[i] = (unsigned char)i;
30242     }
30243     for(i=0; i < sizeof(zSeed) ; i++){
30244       pCtx->j += pCtx->s[i] + zSeed[i];
30245       t = pCtx->s[pCtx->j];
30246       pCtx->s[pCtx->j] = pCtx->s[i];
30247       pCtx->s[i] = t;
30248     }
30249 	pCtx->nMagic = SXPRNG_MAGIC;
30250 
30251 	return SXRET_OK;
30252 }
30253 /*
30254  * Get a single 8-bit random value using the RC4 PRNG.
30255  */
randomByte(SyPRNGCtx * pCtx)30256 static sxu8 randomByte(SyPRNGCtx *pCtx)
30257 {
30258   sxu8 t;
30259 
30260   /* Generate and return single random byte */
30261   pCtx->i++;
30262   t = pCtx->s[pCtx->i];
30263   pCtx->j += t;
30264   pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
30265   pCtx->s[pCtx->j] = t;
30266   t += pCtx->s[pCtx->i];
30267   return pCtx->s[t];
30268 }
SyRandomness(SyPRNGCtx * pCtx,void * pBuf,sxu32 nLen)30269 JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen)
30270 {
30271 	unsigned char *zBuf = (unsigned char *)pBuf;
30272 	unsigned char *zEnd = &zBuf[nLen];
30273 #if defined(UNTRUST)
30274 	if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){
30275 		return SXERR_EMPTY;
30276 	}
30277 #endif
30278 	if(pCtx->nMagic != SXPRNG_MAGIC ){
30279 		return SXERR_CORRUPT;
30280 	}
30281 	for(;;){
30282 		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;
30283 		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;
30284 		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;
30285 		if( zBuf >= zEnd ){break;}	zBuf[0] = randomByte(pCtx);	zBuf++;
30286 	}
30287 	return SXRET_OK;
30288 }
30289 #ifndef JX9_DISABLE_BUILTIN_FUNC
30290 #ifndef JX9_DISABLE_HASH_FUNC
30291 /* SyRunTimeApi: sxhash.c */
30292 /*
30293  * This code implements the MD5 message-digest algorithm.
30294  * The algorithm is due to Ron Rivest.This code was
30295  * written by Colin Plumb in 1993, no copyright is claimed.
30296  * This code is in the public domain; do with it what you wish.
30297  *
30298  * Equivalent code is available from RSA Data Security, Inc.
30299  * This code has been tested against that, and is equivalent,
30300  * except that you don't need to include two pages of legalese
30301  * with every copy.
30302  *
30303  * To compute the message digest of a chunk of bytes, declare an
30304  * MD5Context structure, pass it to MD5Init, call MD5Update as
30305  * needed on buffers full of bytes, and then call MD5Final, which
30306  * will fill a supplied 16-byte array with the digest.
30307  */
30308 #define SX_MD5_BINSZ	16
30309 #define SX_MD5_HEXSZ	32
30310 /*
30311  * Note: this code is harmless on little-endian machines.
30312  */
byteReverse(unsigned char * buf,unsigned longs)30313 static void byteReverse (unsigned char *buf, unsigned longs)
30314 {
30315 	sxu32 t;
30316         do {
30317                 t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
30318                             ((unsigned)buf[1]<<8 | buf[0]);
30319                 *(sxu32*)buf = t;
30320                 buf += 4;
30321         } while (--longs);
30322 }
30323 /* The four core functions - F1 is optimized somewhat */
30324 
30325 /* #define F1(x, y, z) (x & y | ~x & z) */
30326 #ifdef F1
30327 #undef F1
30328 #endif
30329 #ifdef F2
30330 #undef F2
30331 #endif
30332 #ifdef F3
30333 #undef F3
30334 #endif
30335 #ifdef F4
30336 #undef F4
30337 #endif
30338 
30339 #define F1(x, y, z) (z ^ (x & (y ^ z)))
30340 #define F2(x, y, z) F1(z, x, y)
30341 #define F3(x, y, z) (x ^ y ^ z)
30342 #define F4(x, y, z) (y ^ (x | ~z))
30343 
30344 /* This is the central step in the MD5 algorithm.*/
30345 #define SX_MD5STEP(f, w, x, y, z, data, s) \
30346         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
30347 
30348 /*
30349  * The core of the MD5 algorithm, this alters an existing MD5 hash to
30350  * reflect the addition of 16 longwords of new data.MD5Update blocks
30351  * the data and converts bytes into longwords for this routine.
30352  */
MD5Transform(sxu32 buf[4],const sxu32 in[16])30353 static void MD5Transform(sxu32 buf[4], const sxu32 in[16])
30354 {
30355 	register sxu32 a, b, c, d;
30356 
30357         a = buf[0];
30358         b = buf[1];
30359         c = buf[2];
30360         d = buf[3];
30361 
30362         SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
30363         SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
30364         SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
30365         SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
30366         SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
30367         SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
30368         SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
30369         SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
30370         SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
30371         SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
30372         SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
30373         SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
30374         SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
30375         SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
30376         SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
30377         SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
30378 
30379         SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
30380         SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
30381         SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
30382         SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
30383         SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
30384         SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
30385         SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
30386         SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
30387         SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
30388         SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
30389         SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
30390         SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
30391         SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
30392         SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
30393         SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
30394         SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
30395 
30396         SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
30397         SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
30398         SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
30399         SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
30400         SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
30401         SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
30402         SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
30403         SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
30404         SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
30405         SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
30406         SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
30407         SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
30408         SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
30409         SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
30410         SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
30411         SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
30412 
30413         SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
30414         SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
30415         SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
30416         SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
30417         SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
30418         SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
30419         SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
30420         SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
30421         SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
30422         SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
30423         SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
30424         SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
30425         SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
30426         SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
30427         SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
30428         SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
30429 
30430         buf[0] += a;
30431         buf[1] += b;
30432         buf[2] += c;
30433         buf[3] += d;
30434 }
30435 /*
30436  * Update context to reflect the concatenation of another buffer full
30437  * of bytes.
30438  */
MD5Update(MD5Context * ctx,const unsigned char * buf,unsigned int len)30439 JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len)
30440 {
30441 	sxu32 t;
30442 
30443         /* Update bitcount */
30444         t = ctx->bits[0];
30445         if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t)
30446                 ctx->bits[1]++; /* Carry from low to high */
30447         ctx->bits[1] += len >> 29;
30448         t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
30449         /* Handle any leading odd-sized chunks */
30450         if ( t ) {
30451                 unsigned char *p = (unsigned char *)ctx->in + t;
30452 
30453                 t = 64-t;
30454                 if (len < t) {
30455                         SyMemcpy(buf, p, len);
30456                         return;
30457                 }
30458                 SyMemcpy(buf, p, t);
30459                 byteReverse(ctx->in, 16);
30460                 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30461                 buf += t;
30462                 len -= t;
30463         }
30464         /* Process data in 64-byte chunks */
30465         while (len >= 64) {
30466                 SyMemcpy(buf, ctx->in, 64);
30467                 byteReverse(ctx->in, 16);
30468                 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30469                 buf += 64;
30470                 len -= 64;
30471         }
30472         /* Handle any remaining bytes of data.*/
30473         SyMemcpy(buf, ctx->in, len);
30474 }
30475 /*
30476  * Final wrapup - pad to 64-byte boundary with the bit pattern
30477  * 1 0* (64-bit count of bits processed, MSB-first)
30478  */
MD5Final(unsigned char digest[16],MD5Context * ctx)30479 JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){
30480         unsigned count;
30481         unsigned char *p;
30482 
30483         /* Compute number of bytes mod 64 */
30484         count = (ctx->bits[0] >> 3) & 0x3F;
30485 
30486         /* Set the first char of padding to 0x80.This is safe since there is
30487            always at least one byte free */
30488         p = ctx->in + count;
30489         *p++ = 0x80;
30490 
30491         /* Bytes of padding needed to make 64 bytes */
30492         count = 64 - 1 - count;
30493 
30494         /* Pad out to 56 mod 64 */
30495         if (count < 8) {
30496                 /* Two lots of padding:  Pad the first block to 64 bytes */
30497                SyZero(p, count);
30498                 byteReverse(ctx->in, 16);
30499                 MD5Transform(ctx->buf, (sxu32*)ctx->in);
30500 
30501                 /* Now fill the next block with 56 bytes */
30502                 SyZero(ctx->in, 56);
30503         } else {
30504                 /* Pad block to 56 bytes */
30505                 SyZero(p, count-8);
30506         }
30507         byteReverse(ctx->in, 14);
30508 
30509         /* Append length in bits and transform */
30510         ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0];
30511         ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1];
30512 
30513         MD5Transform(ctx->buf, (sxu32*)ctx->in);
30514         byteReverse((unsigned char *)ctx->buf, 4);
30515         SyMemcpy(ctx->buf, digest, 0x10);
30516         SyZero(ctx, sizeof(ctx));    /* In case it's sensitive */
30517 }
30518 #undef F1
30519 #undef F2
30520 #undef F3
30521 #undef F4
MD5Init(MD5Context * pCtx)30522 JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx)
30523 {
30524 	pCtx->buf[0] = 0x67452301;
30525     pCtx->buf[1] = 0xefcdab89;
30526     pCtx->buf[2] = 0x98badcfe;
30527     pCtx->buf[3] = 0x10325476;
30528     pCtx->bits[0] = 0;
30529     pCtx->bits[1] = 0;
30530 
30531    return SXRET_OK;
30532 }
SyMD5Compute(const void * pIn,sxu32 nLen,unsigned char zDigest[16])30533 JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16])
30534 {
30535 	MD5Context sCtx;
30536 	MD5Init(&sCtx);
30537 	MD5Update(&sCtx, (const unsigned char *)pIn, nLen);
30538 	MD5Final(zDigest, &sCtx);
30539 	return SXRET_OK;
30540 }
30541 /*
30542  * SHA-1 in C
30543  * By Steve Reid <steve@edmweb.com>
30544  * Status: Public Domain
30545  */
30546 /*
30547  * blk0() and blk() perform the initial expand.
30548  * I got the idea of expanding during the round function from SSLeay
30549  *
30550  * blk0le() for little-endian and blk0be() for big-endian.
30551  */
30552 #if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
30553 /*
30554  * GCC by itself only generates left rotates.  Use right rotates if
30555  * possible to be kinder to dinky implementations with iterative rotate
30556  * instructions.
30557  */
30558 #define SHA_ROT(op, x, k) \
30559         ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; })
30560 #define rol(x, k) SHA_ROT("roll", x, k)
30561 #define ror(x, k) SHA_ROT("rorl", x, k)
30562 
30563 #else
30564 /* Generic C equivalent */
30565 #define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r))
30566 #define rol(x, k) SHA_ROT(x, k, 32-(k))
30567 #define ror(x, k) SHA_ROT(x, 32-(k), k)
30568 #endif
30569 
30570 #define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \
30571     |(rol(block[i], 8)&0x00FF00FF))
30572 #define blk0be(i) block[i]
30573 #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
30574     ^block[(i+2)&15]^block[i&15], 1))
30575 
30576 /*
30577  * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
30578  *
30579  * Rl0() for little-endian and Rb0() for big-endian.  Endianness is
30580  * determined at run-time.
30581  */
30582 #define Rl0(v, w, x, y, z, i) \
30583     z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30584 #define Rb0(v, w, x, y, z, i) \
30585     z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30586 #define R1(v, w, x, y, z, i) \
30587     z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
30588 #define R2(v, w, x, y, z, i) \
30589     z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2);
30590 #define R3(v, w, x, y, z, i) \
30591     z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2);
30592 #define R4(v, w, x, y, z, i) \
30593     z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2);
30594 
30595 /*
30596  * Hash a single 512-bit block. This is the core of the algorithm.
30597  */
30598 #define a qq[0]
30599 #define b qq[1]
30600 #define c qq[2]
30601 #define d qq[3]
30602 #define e qq[4]
30603 
SHA1Transform(unsigned int state[5],const unsigned char buffer[64])30604 static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
30605 {
30606   unsigned int qq[5]; /* a, b, c, d, e; */
30607   static int one = 1;
30608   unsigned int block[16];
30609   SyMemcpy(buffer, (void *)block, 64);
30610   SyMemcpy(state, qq, 5*sizeof(unsigned int));
30611 
30612   /* Copy context->state[] to working vars */
30613   /*
30614   a = state[0];
30615   b = state[1];
30616   c = state[2];
30617   d = state[3];
30618   e = state[4];
30619   */
30620 
30621   /* 4 rounds of 20 operations each. Loop unrolled. */
30622   if( 1 == *(unsigned char*)&one ){
30623     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);
30624     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);
30625     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);
30626     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);
30627   }else{
30628     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);
30629     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);
30630     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);
30631     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);
30632   }
30633   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);
30634   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);
30635   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);
30636   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);
30637   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);
30638   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);
30639   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);
30640   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);
30641   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);
30642   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);
30643   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);
30644   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);
30645   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);
30646   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);
30647   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);
30648   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);
30649 
30650   /* Add the working vars back into context.state[] */
30651   state[0] += a;
30652   state[1] += b;
30653   state[2] += c;
30654   state[3] += d;
30655   state[4] += e;
30656 }
30657 #undef a
30658 #undef b
30659 #undef c
30660 #undef d
30661 #undef e
30662 /*
30663  * SHA1Init - Initialize new context
30664  */
SHA1Init(SHA1Context * context)30665 JX9_PRIVATE void SHA1Init(SHA1Context *context){
30666     /* SHA1 initialization constants */
30667     context->state[0] = 0x67452301;
30668     context->state[1] = 0xEFCDAB89;
30669     context->state[2] = 0x98BADCFE;
30670     context->state[3] = 0x10325476;
30671     context->state[4] = 0xC3D2E1F0;
30672     context->count[0] = context->count[1] = 0;
30673 }
30674 /*
30675  * Run your data through this.
30676  */
SHA1Update(SHA1Context * context,const unsigned char * data,unsigned int len)30677 JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){
30678     unsigned int i, j;
30679 
30680     j = context->count[0];
30681     if ((context->count[0] += len << 3) < j)
30682 	context->count[1] += (len>>29)+1;
30683     j = (j >> 3) & 63;
30684     if ((j + len) > 63) {
30685 		(void)SyMemcpy(data, &context->buffer[j],  (i = 64-j));
30686 	SHA1Transform(context->state, context->buffer);
30687 	for ( ; i + 63 < len; i += 64)
30688 	    SHA1Transform(context->state, &data[i]);
30689 	j = 0;
30690     } else {
30691 	i = 0;
30692     }
30693 	(void)SyMemcpy(&data[i], &context->buffer[j], len - i);
30694 }
30695 /*
30696  * Add padding and return the message digest.
30697  */
SHA1Final(SHA1Context * context,unsigned char digest[20])30698 JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){
30699     unsigned int i;
30700     unsigned char finalcount[8];
30701 
30702     for (i = 0; i < 8; i++) {
30703 	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
30704 	 >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
30705     }
30706     SHA1Update(context, (const unsigned char *)"\200", 1);
30707     while ((context->count[0] & 504) != 448)
30708 	SHA1Update(context, (const unsigned char *)"\0", 1);
30709     SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
30710 
30711     if (digest) {
30712 	for (i = 0; i < 20; i++)
30713 	    digest[i] = (unsigned char)
30714 		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
30715     }
30716 }
30717 #undef Rl0
30718 #undef Rb0
30719 #undef R1
30720 #undef R2
30721 #undef R3
30722 #undef R4
30723 
SySha1Compute(const void * pIn,sxu32 nLen,unsigned char zDigest[20])30724 JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20])
30725 {
30726 	SHA1Context sCtx;
30727 	SHA1Init(&sCtx);
30728 	SHA1Update(&sCtx, (const unsigned char *)pIn, nLen);
30729 	SHA1Final(&sCtx, zDigest);
30730 	return SXRET_OK;
30731 }
30732 static const sxu32 crc32_table[] = {
30733 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
30734 	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
30735 	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
30736 	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
30737 	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
30738 	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
30739 	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
30740 	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
30741 	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
30742 	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
30743 	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
30744 	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
30745 	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
30746 	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
30747 	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
30748 	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
30749 	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
30750 	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
30751 	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
30752 	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
30753 	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
30754 	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
30755 	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
30756 	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
30757 	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
30758 	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
30759 	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
30760 	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
30761 	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
30762 	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
30763 	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
30764 	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
30765 	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
30766 	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
30767 	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
30768 	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
30769 	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
30770 	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
30771 	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
30772 	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
30773 	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
30774 	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
30775 	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
30776 	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
30777 	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
30778 	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
30779 	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
30780 	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
30781 	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
30782 	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
30783 	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
30784 	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
30785 	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
30786 	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
30787 	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
30788 	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
30789 	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
30790 	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
30791 	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
30792 	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
30793 	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
30794 	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
30795 	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
30796 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
30797 };
30798 #define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) )
SyCrc32Update(sxu32 crc32,const void * pSrc,sxu32 nLen)30799 static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen)
30800 {
30801 	register unsigned char *zIn = (unsigned char *)pSrc;
30802 	unsigned char *zEnd;
30803 	if( zIn == 0 ){
30804 		return crc32;
30805 	}
30806 	zEnd = &zIn[nLen];
30807 	for(;;){
30808 		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30809 		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30810 		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30811 		if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
30812 	}
30813 
30814 	return crc32;
30815 }
SyCrc32(const void * pSrc,sxu32 nLen)30816 JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen)
30817 {
30818 	return SyCrc32Update(SXU32_HIGH, pSrc, nLen);
30819 }
30820 #endif /* JX9_DISABLE_HASH_FUNC */
30821 #endif /* JX9_DISABLE_BUILTIN_FUNC */
30822 #ifndef JX9_DISABLE_BUILTIN_FUNC
SyBinToHexConsumer(const void * pIn,sxu32 nLen,ProcConsumer xConsumer,void * pConsumerData)30823 JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData)
30824 {
30825 	static const unsigned char zHexTab[] = "0123456789abcdef";
30826 	const unsigned char *zIn, *zEnd;
30827 	unsigned char zOut[3];
30828 	sxi32 rc;
30829 #if defined(UNTRUST)
30830 	if( pIn == 0 || xConsumer == 0 ){
30831 		return SXERR_EMPTY;
30832 	}
30833 #endif
30834 	zIn   = (const unsigned char *)pIn;
30835 	zEnd  = &zIn[nLen];
30836 	for(;;){
30837 		if( zIn >= zEnd  ){
30838 			break;
30839 		}
30840 		zOut[0] = zHexTab[zIn[0] >> 4];  zOut[1] = zHexTab[zIn[0] & 0x0F];
30841 		rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData);
30842 		if( rc != SXRET_OK ){
30843 			return rc;
30844 		}
30845 		zIn++;
30846 	}
30847 	return SXRET_OK;
30848 }
30849 #endif /* JX9_DISABLE_BUILTIN_FUNC */
SyBigEndianPack32(unsigned char * buf,sxu32 nb)30850 JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb)
30851 {
30852 	buf[3] = nb & 0xFF ; nb >>=8;
30853 	buf[2] = nb & 0xFF ; nb >>=8;
30854 	buf[1] = nb & 0xFF ; nb >>=8;
30855 	buf[0] = (unsigned char)nb ;
30856 }
SyBigEndianUnpack32(const unsigned char * buf,sxu32 * uNB)30857 JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB)
30858 {
30859 	*uNB = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
30860 }
SyBigEndianPack16(unsigned char * buf,sxu16 nb)30861 JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb)
30862 {
30863 	buf[1] = nb & 0xFF ; nb >>=8;
30864 	buf[0] = (unsigned char)nb ;
30865 }
SyBigEndianUnpack16(const unsigned char * buf,sxu16 * uNB)30866 JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB)
30867 {
30868 	*uNB = buf[1] + (buf[0] << 8);
30869 }
SyBigEndianPack64(unsigned char * buf,sxu64 n64)30870 JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64)
30871 {
30872 	buf[7] = n64 & 0xFF; n64 >>=8;
30873 	buf[6] = n64 & 0xFF; n64 >>=8;
30874 	buf[5] = n64 & 0xFF; n64 >>=8;
30875 	buf[4] = n64 & 0xFF; n64 >>=8;
30876 	buf[3] = n64 & 0xFF; n64 >>=8;
30877 	buf[2] = n64 & 0xFF; n64 >>=8;
30878 	buf[1] = n64 & 0xFF; n64 >>=8;
30879 	buf[0] = (sxu8)n64 ;
30880 }
SyBigEndianUnpack64(const unsigned char * buf,sxu64 * n64)30881 JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64)
30882 {
30883 	sxu32 u1,u2;
30884 	u1 = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
30885 	u2 = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
30886 	*n64 = (((sxu64)u2) << 32) | u1;
30887 }
SyBlobAppendBig64(SyBlob * pBlob,sxu64 n64)30888 JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64)
30889 {
30890 	unsigned char zBuf[8];
30891 	sxi32 rc;
30892 	SyBigEndianPack64(zBuf,n64);
30893 	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30894 	return rc;
30895 }
SyBlobAppendBig32(SyBlob * pBlob,sxu32 n32)30896 JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32)
30897 {
30898 	unsigned char zBuf[4];
30899 	sxi32 rc;
30900 	SyBigEndianPack32(zBuf,n32);
30901 	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30902 	return rc;
30903 }
SyBlobAppendBig16(SyBlob * pBlob,sxu16 n16)30904 JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16)
30905 {
30906 	unsigned char zBuf[2];
30907 	sxi32 rc;
30908 	SyBigEndianPack16(zBuf,n16);
30909 	rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
30910 	return rc;
30911 }
SyTimeFormatToDos(Sytm * pFmt,sxu32 * pOut)30912 JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut)
30913 {
30914 	sxi32 nDate,nTime;
30915 	nDate = ((pFmt->tm_year - 1980) << 9) + (pFmt->tm_mon << 5) + pFmt->tm_mday;
30916 	nTime = (pFmt->tm_hour << 11) + (pFmt->tm_min << 5)+ (pFmt->tm_sec >> 1);
30917 	*pOut = (nDate << 16) | nTime;
30918 }
SyDosTimeFormat(sxu32 nDosDate,Sytm * pOut)30919 JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut)
30920 {
30921 	sxu16 nDate;
30922 	sxu16 nTime;
30923 	nDate = nDosDate >> 16;
30924 	nTime = nDosDate & 0xFFFF;
30925 	pOut->tm_isdst  = 0;
30926 	pOut->tm_year 	= 1980 + (nDate >> 9);
30927 	pOut->tm_mon	= (nDate % (1<<9))>>5;
30928 	pOut->tm_mday	= (nDate % (1<<9))&0x1F;
30929 	pOut->tm_hour	= nTime >> 11;
30930 	pOut->tm_min	= (nTime % (1<<11)) >> 5;
30931 	pOut->tm_sec	= ((nTime % (1<<11))& 0x1F )<<1;
30932 }
30933 
30934 /* jx9_memobj.c */
30935 /*
30936  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
30937  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
30938  * Version 1.7.2
30939  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
30940  * please contact Symisc Systems via:
30941  *       legal@symisc.net
30942  *       licensing@symisc.net
30943  *       contact@symisc.net
30944  * or visit:
30945  *      http://jx9.symisc.net/
30946  */
30947  /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
30948 #ifndef JX9_AMALGAMATION
30949 #include "jx9Int.h"
30950 #endif
30951 /* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */
30952 /*
30953  * Notes on memory objects [i.e: jx9_value].
30954  * Internally, the JX9 virtual machine manipulates nearly all JX9 values
30955  * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures.
30956  * Each jx9_values struct may cache multiple representations (string,
30957  * integer etc.) of the same value.
30958  */
30959 /*
30960  * Convert a 64-bit IEEE double into a 64-bit signed integer.
30961  * If the double is too large, return 0x8000000000000000.
30962  *
30963  * Most systems appear to do this simply by assigning ariables and without
30964  * the extra range tests.
30965  * But there are reports that windows throws an expection if the floating
30966  * point value is out of range.
30967  */
MemObjRealToInt(jx9_value * pObj)30968 static sxi64 MemObjRealToInt(jx9_value *pObj)
30969 {
30970 #ifdef JX9_OMIT_FLOATING_POINT
30971 	/* Real and 64bit integer are the same when floating point arithmetic
30972 	 * is omitted from the build.
30973 	 */
30974 	return pObj->x.rVal;
30975 #else
30976  /*
30977   ** Many compilers we encounter do not define constants for the
30978   ** minimum and maximum 64-bit integers, or they define them
30979   ** inconsistently.  And many do not understand the "LL" notation.
30980   ** So we define our own static constants here using nothing
30981   ** larger than a 32-bit integer constant.
30982   */
30983   static const sxi64 maxInt = LARGEST_INT64;
30984   static const sxi64 minInt = SMALLEST_INT64;
30985   jx9_real r = pObj->x.rVal;
30986   if( r<(jx9_real)minInt ){
30987     return minInt;
30988   }else if( r>(jx9_real)maxInt ){
30989     /* minInt is correct here - not maxInt.  It turns out that assigning
30990     ** a very large positive number to an integer results in a very large
30991     ** negative integer.  This makes no sense, but it is what x86 hardware
30992     ** does so for compatibility we will do the same in software. */
30993     return minInt;
30994   }else{
30995     return (sxi64)r;
30996   }
30997 #endif
30998 }
30999 /*
31000  * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal]
31001  * to a 64-bit integer.
31002  */
jx9TokenValueToInt64(SyString * pVal)31003 JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal)
31004 {
31005 	sxi64 iVal = 0;
31006 	if( pVal->nByte <= 0 ){
31007 		return 0;
31008 	}
31009 	if( pVal->zString[0] == '0' ){
31010 		sxi32 c;
31011 		if( pVal->nByte == sizeof(char) ){
31012 			return 0;
31013 		}
31014 		c = pVal->zString[1];
31015 		if( c  == 'x' || c == 'X' ){
31016 			/* Hex digit stream */
31017 			SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31018 		}else if( c == 'b' || c == 'B' ){
31019 			/* Binary digit stream */
31020 			SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31021 		}else{
31022 			/* Octal digit stream */
31023 			SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31024 		}
31025 	}else{
31026 		/* Decimal digit stream */
31027 		SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
31028 	}
31029 	return iVal;
31030 }
31031 /*
31032  * Return some kind of 64-bit integer value which is the best we can
31033  * do at representing the value that pObj describes as a string
31034  * representation.
31035  */
MemObjStringToInt(jx9_value * pObj)31036 static sxi64 MemObjStringToInt(jx9_value *pObj)
31037 {
31038 	SyString sVal;
31039 	SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31040 	return jx9TokenValueToInt64(&sVal);
31041 }
31042 /*
31043  * Return some kind of integer value which is the best we can
31044  * do at representing the value that pObj describes as an integer.
31045  * If pObj is an integer, then the value is exact. If pObj is
31046  * a floating-point then  the value returned is the integer part.
31047  * If pObj is a string, then we make an attempt to convert it into
31048  * a integer and return that.
31049  * If pObj represents a NULL value, return 0.
31050  */
MemObjIntValue(jx9_value * pObj)31051 static sxi64 MemObjIntValue(jx9_value *pObj)
31052 {
31053 	sxi32 iFlags;
31054 	iFlags = pObj->iFlags;
31055 	if (iFlags & MEMOBJ_REAL ){
31056 		return MemObjRealToInt(&(*pObj));
31057 	}else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
31058 		return pObj->x.iVal;
31059 	}else if (iFlags & MEMOBJ_STRING) {
31060 		return MemObjStringToInt(&(*pObj));
31061 	}else if( iFlags & MEMOBJ_NULL ){
31062 		return 0;
31063 	}else if( iFlags & MEMOBJ_HASHMAP ){
31064 		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31065 		sxu32 n = pMap->nEntry;
31066 		jx9HashmapUnref(pMap);
31067 		/* Return total number of entries in the hashmap */
31068 		return n;
31069 	}else if(iFlags & MEMOBJ_RES ){
31070 		return pObj->x.pOther != 0;
31071 	}
31072 	/* CANT HAPPEN */
31073 	return 0;
31074 }
31075 /*
31076  * Return some kind of real value which is the best we can
31077  * do at representing the value that pObj describes as a real.
31078  * If pObj is a real, then the value is exact.If pObj is an
31079  * integer then the integer  is promoted to real and that value
31080  * is returned.
31081  * If pObj is a string, then we make an attempt to convert it
31082  * into a real and return that.
31083  * If pObj represents a NULL value, return 0.0
31084  */
MemObjRealValue(jx9_value * pObj)31085 static jx9_real MemObjRealValue(jx9_value *pObj)
31086 {
31087 	sxi32 iFlags;
31088 	iFlags = pObj->iFlags;
31089 	if( iFlags & MEMOBJ_REAL ){
31090 		return pObj->x.rVal;
31091 	}else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
31092 		return (jx9_real)pObj->x.iVal;
31093 	}else if (iFlags & MEMOBJ_STRING){
31094 		SyString sString;
31095 #ifdef JX9_OMIT_FLOATING_POINT
31096 		jx9_real rVal = 0;
31097 #else
31098 		jx9_real rVal = 0.0;
31099 #endif
31100 		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31101 		if( SyBlobLength(&pObj->sBlob) > 0 ){
31102 			/* Convert as much as we can */
31103 #ifdef JX9_OMIT_FLOATING_POINT
31104 			rVal = MemObjStringToInt(&(*pObj));
31105 #else
31106 			SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
31107 #endif
31108 		}
31109 		return rVal;
31110 	}else if( iFlags & MEMOBJ_NULL ){
31111 #ifdef JX9_OMIT_FLOATING_POINT
31112 		return 0;
31113 #else
31114 		return 0.0;
31115 #endif
31116 	}else if( iFlags & MEMOBJ_HASHMAP ){
31117 		/* Return the total number of entries in the hashmap */
31118 		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31119 		jx9_real n = (jx9_real)pMap->nEntry;
31120 		jx9HashmapUnref(pMap);
31121 		return n;
31122 	}else if(iFlags & MEMOBJ_RES ){
31123 		return (jx9_real)(pObj->x.pOther != 0);
31124 	}
31125 	/* NOT REACHED  */
31126 	return 0;
31127 }
31128 /*
31129  * Return the string representation of a given jx9_value.
31130  * This function never fail and always return SXRET_OK.
31131  */
MemObjStringValue(SyBlob * pOut,jx9_value * pObj)31132 static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj)
31133 {
31134 	if( pObj->iFlags & MEMOBJ_REAL ){
31135 		SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
31136 	}else if( pObj->iFlags & MEMOBJ_INT ){
31137 		SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal);
31138 		/* %qd (BSD quad) is equivalent to %lld in the libc printf */
31139 	}else if( pObj->iFlags & MEMOBJ_BOOL ){
31140 		if( pObj->x.iVal ){
31141 			SyBlobAppend(&(*pOut),"true", sizeof("true")-1);
31142 		}else{
31143 			SyBlobAppend(&(*pOut),"false", sizeof("false")-1);
31144 		}
31145 	}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
31146 		/* Serialize JSON object or array */
31147 		jx9JsonSerialize(pObj,pOut);
31148 		jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
31149 	}else if(pObj->iFlags & MEMOBJ_RES ){
31150 		SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther);
31151 	}
31152 	return SXRET_OK;
31153 }
31154 /*
31155  * Return some kind of boolean value which is the best we can do
31156  * at representing the value that pObj describes as a boolean.
31157  * When converting to boolean, the following values are considered FALSE:
31158  * NULL
31159  * the boolean FALSE itself.
31160  * the integer 0 (zero).
31161  * the real 0.0 (zero).
31162  * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string
31163  * "false".
31164  * an array with zero elements.
31165  */
MemObjBooleanValue(jx9_value * pObj)31166 static sxi32 MemObjBooleanValue(jx9_value *pObj)
31167 {
31168 	sxi32 iFlags;
31169 	iFlags = pObj->iFlags;
31170 	if (iFlags & MEMOBJ_REAL ){
31171 #ifdef JX9_OMIT_FLOATING_POINT
31172 		return pObj->x.rVal ? 1 : 0;
31173 #else
31174 		return pObj->x.rVal != 0.0 ? 1 : 0;
31175 #endif
31176 	}else if( iFlags & MEMOBJ_INT ){
31177 		return pObj->x.iVal ? 1 : 0;
31178 	}else if (iFlags & MEMOBJ_STRING) {
31179 		SyString sString;
31180 		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31181 		if( sString.nByte == 0 ){
31182 			/* Empty string */
31183 			return 0;
31184 		}else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) ||
31185 			(sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) ||
31186 			(sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){
31187 				return 1;
31188 		}else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){
31189 			return 0;
31190 		}else{
31191 			const char *zIn, *zEnd;
31192 			zIn = sString.zString;
31193 			zEnd = &zIn[sString.nByte];
31194 			while( zIn < zEnd && zIn[0] == '0' ){
31195 				zIn++;
31196 			}
31197 			return zIn >= zEnd ? 0 : 1;
31198 		}
31199 	}else if( iFlags & MEMOBJ_NULL ){
31200 		return 0;
31201 	}else if( iFlags & MEMOBJ_HASHMAP ){
31202 		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31203 		sxu32 n = pMap->nEntry;
31204 		jx9HashmapUnref(pMap);
31205 		return n > 0 ? TRUE : FALSE;
31206 	}else if(iFlags & MEMOBJ_RES ){
31207 		return pObj->x.pOther != 0;
31208 	}
31209 	/* NOT REACHED */
31210 	return 0;
31211 }
31212 /*
31213  * If the jx9_value is of type real, try to make it an integer also.
31214  */
MemObjTryIntger(jx9_value * pObj)31215 static sxi32 MemObjTryIntger(jx9_value *pObj)
31216 {
31217 	sxi64 iVal = MemObjRealToInt(&(*pObj));
31218   /* Only mark the value as an integer if
31219   **
31220   **    (1) the round-trip conversion real->int->real is a no-op, and
31221   **    (2) The integer is neither the largest nor the smallest
31222   **        possible integer
31223   **
31224   ** The second and third terms in the following conditional enforces
31225   ** the second condition under the assumption that addition overflow causes
31226   ** values to wrap around.  On x86 hardware, the third term is always
31227   ** true and could be omitted.  But we leave it in because other
31228   ** architectures might behave differently.
31229   */
31230 	if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){
31231 		pObj->x.iVal = iVal;
31232 		pObj->iFlags = MEMOBJ_INT;
31233 	}
31234 	return SXRET_OK;
31235 }
31236 /*
31237  * Convert a jx9_value to type integer.Invalidate any prior representations.
31238  */
jx9MemObjToInteger(jx9_value * pObj)31239 JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj)
31240 {
31241 	if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
31242 		/* Preform the conversion */
31243 		pObj->x.iVal = MemObjIntValue(&(*pObj));
31244 		/* Invalidate any prior representations */
31245 		SyBlobRelease(&pObj->sBlob);
31246 		MemObjSetType(pObj, MEMOBJ_INT);
31247 	}
31248 	return SXRET_OK;
31249 }
31250 /*
31251  * Convert a jx9_value to type real (Try to get an integer representation also).
31252  * Invalidate any prior representations
31253  */
jx9MemObjToReal(jx9_value * pObj)31254 JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj)
31255 {
31256 	if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
31257 		/* Preform the conversion */
31258 		pObj->x.rVal = MemObjRealValue(&(*pObj));
31259 		/* Invalidate any prior representations */
31260 		SyBlobRelease(&pObj->sBlob);
31261 		MemObjSetType(pObj, MEMOBJ_REAL);
31262 	}
31263 	return SXRET_OK;
31264 }
31265 /*
31266  * Convert a jx9_value to type boolean.Invalidate any prior representations.
31267  */
jx9MemObjToBool(jx9_value * pObj)31268 JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj)
31269 {
31270 	if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
31271 		/* Preform the conversion */
31272 		pObj->x.iVal = MemObjBooleanValue(&(*pObj));
31273 		/* Invalidate any prior representations */
31274 		SyBlobRelease(&pObj->sBlob);
31275 		MemObjSetType(pObj, MEMOBJ_BOOL);
31276 	}
31277 	return SXRET_OK;
31278 }
31279 /*
31280  * Convert a jx9_value to type string.Prior representations are NOT invalidated.
31281  */
jx9MemObjToString(jx9_value * pObj)31282 JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj)
31283 {
31284 	sxi32 rc = SXRET_OK;
31285 	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31286 		/* Perform the conversion */
31287 		SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
31288 		rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
31289 		MemObjSetType(pObj, MEMOBJ_STRING);
31290 	}
31291 	return rc;
31292 }
31293 /*
31294  * Nullify a jx9_value.In other words invalidate any prior
31295  * representation.
31296  */
jx9MemObjToNull(jx9_value * pObj)31297 JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj)
31298 {
31299 	return jx9MemObjRelease(pObj);
31300 }
31301 /*
31302  * Convert a jx9_value to type array.Invalidate any prior representations.
31303   * According to the JX9 language reference manual.
31304   *   For any of the types: integer, float, string, boolean converting a value
31305   *   to an array results in an array with a single element with index zero
31306   *   and the value of the scalar which was converted.
31307   */
jx9MemObjToHashmap(jx9_value * pObj)31308 JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj)
31309 {
31310 	if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
31311 		jx9_hashmap *pMap;
31312 		/* Allocate a new hashmap instance */
31313 		pMap = jx9NewHashmap(pObj->pVm, 0, 0);
31314 		if( pMap == 0 ){
31315 			return SXERR_MEM;
31316 		}
31317 		if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
31318 			/*
31319 			 * According to the JX9 language reference manual.
31320 			 *   For any of the types: integer, float, string, boolean converting a value
31321 			 *   to an array results in an array with a single element with index zero
31322 			 *   and the value of the scalar which was converted.
31323 			 */
31324 			/* Insert a single element */
31325 			jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj));
31326 			SyBlobRelease(&pObj->sBlob);
31327 		}
31328 		/* Invalidate any prior representation */
31329 		MemObjSetType(pObj, MEMOBJ_HASHMAP);
31330 		pObj->x.pOther = pMap;
31331 	}
31332 	return SXRET_OK;
31333 }
31334 /*
31335  * Return a pointer to the appropriate convertion method associated
31336  * with the given type.
31337  * Note on type juggling.
31338  * Accoding to the JX9 language reference manual
31339  *  JX9 does not require (or support) explicit type definition in variable
31340  *  declaration; a variable's type is determined by the context in which
31341  *  the variable is used. That is to say, if a string value is assigned
31342  *  to variable $var, $var becomes a string. If an integer value is then
31343  *  assigned to $var, it becomes an integer.
31344  */
jx9MemObjCastMethod(sxi32 iFlags)31345 JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags)
31346 {
31347 	if( iFlags & MEMOBJ_STRING ){
31348 		return jx9MemObjToString;
31349 	}else if( iFlags & MEMOBJ_INT ){
31350 		return jx9MemObjToInteger;
31351 	}else if( iFlags & MEMOBJ_REAL ){
31352 		return jx9MemObjToReal;
31353 	}else if( iFlags & MEMOBJ_BOOL ){
31354 		return jx9MemObjToBool;
31355 	}else if( iFlags & MEMOBJ_HASHMAP ){
31356 		return jx9MemObjToHashmap;
31357 	}
31358 	/* NULL cast */
31359 	return jx9MemObjToNull;
31360 }
31361 /*
31362  * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks
31363  * like a numeric number [i.e: if the jx9_value is of type string.].
31364  * Return TRUE if numeric.FALSE otherwise.
31365  */
jx9MemObjIsNumeric(jx9_value * pObj)31366 JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj)
31367 {
31368 	if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
31369 		return TRUE;
31370 	}else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
31371 		return FALSE;
31372 	}else if( pObj->iFlags & MEMOBJ_STRING ){
31373 		SyString sStr;
31374 		sxi32 rc;
31375 		SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31376 		if( sStr.nByte <= 0 ){
31377 			/* Empty string */
31378 			return FALSE;
31379 		}
31380 		/* Check if the string representation looks like a numeric number */
31381 		rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0);
31382 		return rc == SXRET_OK ? TRUE : FALSE;
31383 	}
31384 	/* NOT REACHED */
31385 	return FALSE;
31386 }
31387 /*
31388  * Check whether the jx9_value is empty.Return TRUE if empty.
31389  * FALSE otherwise.
31390  * An jx9_value is considered empty if the following are true:
31391  * NULL value.
31392  * Boolean FALSE.
31393  * Integer/Float with a 0 (zero) value.
31394  * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...].
31395  * An empty array.
31396  * NOTE
31397  *  OBJECT VALUE MUST NOT BE MODIFIED.
31398  */
jx9MemObjIsEmpty(jx9_value * pObj)31399 JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj)
31400 {
31401 	if( pObj->iFlags & MEMOBJ_NULL ){
31402 		return TRUE;
31403 	}else if( pObj->iFlags & MEMOBJ_INT ){
31404 		return pObj->x.iVal == 0 ? TRUE : FALSE;
31405 	}else if( pObj->iFlags & MEMOBJ_REAL ){
31406 		return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE;
31407 	}else if( pObj->iFlags & MEMOBJ_BOOL ){
31408 		return !pObj->x.iVal;
31409 	}else if( pObj->iFlags & MEMOBJ_STRING ){
31410 		if( SyBlobLength(&pObj->sBlob) <= 0 ){
31411 			return TRUE;
31412 		}else{
31413 			const char *zIn, *zEnd;
31414 			zIn = (const char *)SyBlobData(&pObj->sBlob);
31415 			zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
31416 			while( zIn < zEnd ){
31417 				if( zIn[0] != '0' ){
31418 					break;
31419 				}
31420 				zIn++;
31421 			}
31422 			return zIn >= zEnd ? TRUE : FALSE;
31423 		}
31424 	}else if( pObj->iFlags & MEMOBJ_HASHMAP ){
31425 		jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31426 		return pMap->nEntry == 0 ? TRUE : FALSE;
31427 	}else if ( pObj->iFlags & (MEMOBJ_RES) ){
31428 		return FALSE;
31429 	}
31430 	/* Assume empty by default */
31431 	return TRUE;
31432 }
31433 /*
31434  * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
31435  * or both.
31436  * Invalidate any prior representations. Every effort is made to force
31437  * the conversion, even if the input is a string that does not look
31438  * completely like a number.Convert as much of the string as we can
31439  * and ignore the rest.
31440  */
jx9MemObjToNumeric(jx9_value * pObj)31441 JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj)
31442 {
31443 	if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
31444 		if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
31445 			if( pObj->iFlags & MEMOBJ_NULL ){
31446 				pObj->x.iVal = 0;
31447 			}
31448 			MemObjSetType(pObj, MEMOBJ_INT);
31449 		}
31450 		/* Already numeric */
31451 		return  SXRET_OK;
31452 	}
31453 	if( pObj->iFlags & MEMOBJ_STRING ){
31454 		sxi32 rc = SXERR_INVALID;
31455 		sxu8 bReal = FALSE;
31456 		SyString sString;
31457 		SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
31458 		/* Check if the given string looks like a numeric number */
31459 		if( sString.nByte > 0 ){
31460 			rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0);
31461 		}
31462 		if( bReal ){
31463 			jx9MemObjToReal(&(*pObj));
31464 		}else{
31465 			if( rc != SXRET_OK ){
31466 				/* The input does not look at all like a number, set the value to 0 */
31467 				pObj->x.iVal = 0;
31468 			}else{
31469 				/* Convert as much as we can */
31470 				pObj->x.iVal = MemObjStringToInt(&(*pObj));
31471 			}
31472 			MemObjSetType(pObj, MEMOBJ_INT);
31473 			SyBlobRelease(&pObj->sBlob);
31474 		}
31475 	}else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){
31476 		jx9MemObjToInteger(pObj);
31477 	}else{
31478 		/* Perform a blind cast */
31479 		jx9MemObjToReal(&(*pObj));
31480 	}
31481 	return SXRET_OK;
31482 }
31483 /*
31484  * Try a get an integer representation of the given jx9_value.
31485  * If the jx9_value is not of type real, this function is a no-op.
31486  */
jx9MemObjTryInteger(jx9_value * pObj)31487 JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj)
31488 {
31489 	if( pObj->iFlags & MEMOBJ_REAL ){
31490 		/* Work only with reals */
31491 		MemObjTryIntger(&(*pObj));
31492 	}
31493 	return SXRET_OK;
31494 }
31495 /*
31496  * Initialize a jx9_value to the null type.
31497  */
jx9MemObjInit(jx9_vm * pVm,jx9_value * pObj)31498 JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj)
31499 {
31500 	/* Zero the structure */
31501 	SyZero(pObj, sizeof(jx9_value));
31502 	/* Initialize fields */
31503 	pObj->pVm = pVm;
31504 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31505 	/* Set the NULL type */
31506 	pObj->iFlags = MEMOBJ_NULL;
31507 	return SXRET_OK;
31508 }
31509 /*
31510  * Initialize a jx9_value to the integer type.
31511  */
jx9MemObjInitFromInt(jx9_vm * pVm,jx9_value * pObj,sxi64 iVal)31512 JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal)
31513 {
31514 	/* Zero the structure */
31515 	SyZero(pObj, sizeof(jx9_value));
31516 	/* Initialize fields */
31517 	pObj->pVm = pVm;
31518 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31519 	/* Set the desired type */
31520 	pObj->x.iVal = iVal;
31521 	pObj->iFlags = MEMOBJ_INT;
31522 	return SXRET_OK;
31523 }
31524 /*
31525  * Initialize a jx9_value to the boolean type.
31526  */
jx9MemObjInitFromBool(jx9_vm * pVm,jx9_value * pObj,sxi32 iVal)31527 JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal)
31528 {
31529 	/* Zero the structure */
31530 	SyZero(pObj, sizeof(jx9_value));
31531 	/* Initialize fields */
31532 	pObj->pVm = pVm;
31533 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31534 	/* Set the desired type */
31535 	pObj->x.iVal = iVal ? 1 : 0;
31536 	pObj->iFlags = MEMOBJ_BOOL;
31537 	return SXRET_OK;
31538 }
31539 #if 0
31540 /*
31541  * Initialize a jx9_value to the real type.
31542  */
31543 JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal)
31544 {
31545 	/* Zero the structure */
31546 	SyZero(pObj, sizeof(jx9_value));
31547 	/* Initialize fields */
31548 	pObj->pVm = pVm;
31549 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31550 	/* Set the desired type */
31551 	pObj->x.rVal = rVal;
31552 	pObj->iFlags = MEMOBJ_REAL;
31553 	return SXRET_OK;
31554 }
31555 #endif
31556 /*
31557  * Initialize a jx9_value to the array type.
31558  */
jx9MemObjInitFromArray(jx9_vm * pVm,jx9_value * pObj,jx9_hashmap * pArray)31559 JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray)
31560 {
31561 	/* Zero the structure */
31562 	SyZero(pObj, sizeof(jx9_value));
31563 	/* Initialize fields */
31564 	pObj->pVm = pVm;
31565 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31566 	/* Set the desired type */
31567 	pObj->iFlags = MEMOBJ_HASHMAP;
31568 	pObj->x.pOther = pArray;
31569 	return SXRET_OK;
31570 }
31571 /*
31572  * Initialize a jx9_value to the string type.
31573  */
jx9MemObjInitFromString(jx9_vm * pVm,jx9_value * pObj,const SyString * pVal)31574 JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal)
31575 {
31576 	/* Zero the structure */
31577 	SyZero(pObj, sizeof(jx9_value));
31578 	/* Initialize fields */
31579 	pObj->pVm = pVm;
31580 	SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
31581 	if( pVal ){
31582 		/* Append contents */
31583 		SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte);
31584 	}
31585 	/* Set the desired type */
31586 	pObj->iFlags = MEMOBJ_STRING;
31587 	return SXRET_OK;
31588 }
31589 /*
31590  * Append some contents to the internal buffer of a given jx9_value.
31591  * If the given jx9_value is not of type string, this function
31592  * invalidate any prior representation and set the string type.
31593  * Then a simple append operation is performed.
31594  */
jx9MemObjStringAppend(jx9_value * pObj,const char * zData,sxu32 nLen)31595 JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen)
31596 {
31597 	sxi32 rc;
31598 	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31599 		/* Invalidate any prior representation */
31600 		jx9MemObjRelease(pObj);
31601 		MemObjSetType(pObj, MEMOBJ_STRING);
31602 	}
31603 	/* Append contents */
31604 	rc = SyBlobAppend(&pObj->sBlob, zData, nLen);
31605 	return rc;
31606 }
31607 #if 0
31608 /*
31609  * Format and append some contents to the internal buffer of a given jx9_value.
31610  * If the given jx9_value is not of type string, this function invalidate
31611  * any prior representation and set the string type.
31612  * Then a simple format and append operation is performed.
31613  */
31614 JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap)
31615 {
31616 	sxi32 rc;
31617 	if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
31618 		/* Invalidate any prior representation */
31619 		jx9MemObjRelease(pObj);
31620 		MemObjSetType(pObj, MEMOBJ_STRING);
31621 	}
31622 	/* Format and append contents */
31623 	rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap);
31624 	return rc;
31625 }
31626 #endif
31627 /*
31628  * Duplicate the contents of a jx9_value.
31629  */
jx9MemObjStore(jx9_value * pSrc,jx9_value * pDest)31630 JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest)
31631 {
31632 	jx9_hashmap *pMap = 0;
31633 	sxi32 rc;
31634 	if( pSrc->iFlags & MEMOBJ_HASHMAP ){
31635 		/* Increment reference count */
31636 		((jx9_hashmap *)pSrc->x.pOther)->iRef++;
31637 	}
31638 	if( pDest->iFlags & MEMOBJ_HASHMAP ){
31639 		pMap = (jx9_hashmap *)pDest->x.pOther;
31640 	}
31641 	SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
31642 	rc = SXRET_OK;
31643 	if( SyBlobLength(&pSrc->sBlob) > 0 ){
31644 		SyBlobReset(&pDest->sBlob);
31645 		rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob);
31646 	}else{
31647 		if( SyBlobLength(&pDest->sBlob) > 0 ){
31648 			SyBlobRelease(&pDest->sBlob);
31649 		}
31650 	}
31651 	if( pMap ){
31652 		jx9HashmapUnref(pMap);
31653 	}
31654 	return rc;
31655 }
31656 /*
31657  * Duplicate the contents of a jx9_value but do not copy internal
31658  * buffer contents, simply point to it.
31659  */
jx9MemObjLoad(jx9_value * pSrc,jx9_value * pDest)31660 JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest)
31661 {
31662 	SyMemcpy((const void *)&(*pSrc), &(*pDest),
31663 		sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
31664 	if( pSrc->iFlags & MEMOBJ_HASHMAP ){
31665 		/* Increment reference count */
31666 		((jx9_hashmap *)pSrc->x.pOther)->iRef++;
31667 	}
31668 	if( SyBlobLength(&pDest->sBlob) > 0 ){
31669 		SyBlobRelease(&pDest->sBlob);
31670 	}
31671 	if( SyBlobLength(&pSrc->sBlob) > 0 ){
31672 		SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob));
31673 	}
31674 	return SXRET_OK;
31675 }
31676 /*
31677  * Invalidate any prior representation of a given jx9_value.
31678  */
jx9MemObjRelease(jx9_value * pObj)31679 JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj)
31680 {
31681 	if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
31682 		if( pObj->iFlags & MEMOBJ_HASHMAP ){
31683 			jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
31684 		}
31685 		/* Release the internal buffer */
31686 		SyBlobRelease(&pObj->sBlob);
31687 		/* Invalidate any prior representation */
31688 		pObj->iFlags = MEMOBJ_NULL;
31689 	}
31690 	return SXRET_OK;
31691 }
31692 /*
31693  * Compare two jx9_values.
31694  * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
31695  * or < 0 if pObj2 is greater than pObj1.
31696  * Type comparison table taken from the JX9 language reference manual.
31697  * Comparisons of $x with JX9 functions Expression
31698  *              gettype() 	empty() 	is_null() 	isset() 	boolean : if($x)
31699  * $x = ""; 	string 	    TRUE 	FALSE 	TRUE 	FALSE
31700  * $x = null 	NULL 	    TRUE 	TRUE 	FALSE 	FALSE
31701  * var $x; 	    NULL 	TRUE 	TRUE 	FALSE 	FALSE
31702  * $x is undefined 	NULL 	TRUE 	TRUE 	FALSE 	FALSE
31703  *  $x = array(); 	array 	TRUE 	FALSE 	TRUE 	FALSE
31704  * $x = false; 	boolean 	TRUE 	FALSE 	TRUE 	FALSE
31705  * $x = true; 	boolean 	FALSE 	FALSE 	TRUE 	TRUE
31706  * $x = 1; 	    integer 	FALSE 	FALSE 	TRUE 	TRUE
31707  * $x = 42; 	integer 	FALSE 	FALSE 	TRUE 	TRUE
31708  * $x = 0; 	    integer 	TRUE 	FALSE 	TRUE 	FALSE
31709  * $x = -1; 	integer 	FALSE 	FALSE 	TRUE 	TRUE
31710  * $x = "1"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
31711  * $x = "0"; 	string 	TRUE 	FALSE 	TRUE 	FALSE
31712  * $x = "-1"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
31713  * $x = "jx9"; 	string 	FALSE 	FALSE 	TRUE 	TRUE
31714  * $x = "true"; string 	FALSE 	FALSE 	TRUE 	TRUE
31715  * $x = "false"; string 	FALSE 	FALSE 	TRUE 	TRUE
31716  *      Loose comparisons with ==
31717  * TRUE 	FALSE 	1 	0 	-1 	"1" 	"0" 	"-1" 	NULL 	array() 	"jx9" 	""
31718  * TRUE 	TRUE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE
31719  * FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE
31720  * 1 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31721  * 0 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	TRUE 	TRUE
31722  * -1 	TRUE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE
31723  * "1" 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31724  * "0" 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31725  * "-1" 	TRUE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE
31726  * NULL 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	TRUE 	FALSE 	TRUE
31727  * array() 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	TRUE 	FALSE 	FALSE
31728  * "jx9" 	TRUE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE
31729  * "" 	FALSE 	TRUE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	TRUE
31730  *    Strict comparisons with ===
31731  * TRUE 	FALSE 	1 	0 	-1 	"1" 	"0" 	"-1" 	NULL 	array() 	"jx9" 	""
31732  * TRUE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31733  * FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31734  * 1 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31735  * 0 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31736  * -1 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31737  * "1" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31738  * "0" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE
31739  * "-1" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE 	FALSE
31740  * NULL 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE 	FALSE
31741  * array() 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE 	FALSE
31742  * "jx9" 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE 	FALSE
31743  * "" 	    FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	FALSE 	TRUE
31744  */
jx9MemObjCmp(jx9_value * pObj1,jx9_value * pObj2,int bStrict,int iNest)31745 JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest)
31746 {
31747 	sxi32 iComb;
31748 	sxi32 rc;
31749 	if( bStrict ){
31750 		sxi32 iF1, iF2;
31751 		/* Strict comparisons with === */
31752 		iF1 = pObj1->iFlags;
31753 		iF2 = pObj2->iFlags;
31754 		if( iF1 != iF2 ){
31755 			/* Not of the same type */
31756 			return 1;
31757 		}
31758 	}
31759 	/* Combine flag together */
31760 	iComb = pObj1->iFlags|pObj2->iFlags;
31761 	if( iComb & (MEMOBJ_RES|MEMOBJ_BOOL) ){
31762 		/* Convert to boolean: Keep in mind FALSE < TRUE */
31763 		if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
31764 			jx9MemObjToBool(pObj1);
31765 		}
31766 		if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
31767 			jx9MemObjToBool(pObj2);
31768 		}
31769 		return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
31770 	}else if( iComb & MEMOBJ_NULL ){
31771         if( (pObj1->iFlags & MEMOBJ_NULL) == 0 ){
31772             return 1;
31773         }
31774         if( (pObj2->iFlags & MEMOBJ_NULL) == 0 ){
31775             return -1;
31776         }
31777     }else if ( iComb & MEMOBJ_HASHMAP ){
31778 		/* Hashmap aka 'array' comparison */
31779 		if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
31780 			/* Array is always greater */
31781 			return -1;
31782 		}
31783 		if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
31784 			/* Array is always greater */
31785 			return 1;
31786 		}
31787 		/* Perform the comparison */
31788 		rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict);
31789 		return rc;
31790 	}else if ( iComb & MEMOBJ_STRING ){
31791 		SyString s1, s2;
31792 		/* Perform a strict string comparison.*/
31793 		if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
31794 			jx9MemObjToString(pObj1);
31795 		}
31796 		if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
31797 			jx9MemObjToString(pObj2);
31798 		}
31799 		SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob));
31800 		SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob));
31801 		/*
31802 		 * Strings are compared using memcmp(). If one value is an exact prefix of the
31803 		 * other, then the shorter value is less than the longer value.
31804 		 */
31805 		rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte));
31806 		if( rc == 0 ){
31807 			if( s1.nByte != s2.nByte ){
31808 				rc = s1.nByte < s2.nByte ? -1 : 1;
31809 			}
31810 		}
31811 		return rc;
31812 	}else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
31813 		/* Perform a numeric comparison if one of the operand is numeric(integer or real) */
31814 		if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
31815 			jx9MemObjToNumeric(pObj1);
31816 		}
31817 		if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
31818 			jx9MemObjToNumeric(pObj2);
31819 		}
31820 		if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
31821 			jx9_real r1, r2;
31822 			/* Compare as reals */
31823 			if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
31824 				jx9MemObjToReal(pObj1);
31825 			}
31826 			r1 = pObj1->x.rVal;
31827 			if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
31828 				jx9MemObjToReal(pObj2);
31829 			}
31830 			r2 = pObj2->x.rVal;
31831 			if( r1 > r2 ){
31832 				return 1;
31833 			}else if( r1 < r2 ){
31834 				return -1;
31835 			}
31836 			return 0;
31837 		}else{
31838 			/* Integer comparison */
31839 			if( pObj1->x.iVal > pObj2->x.iVal ){
31840 				return 1;
31841 			}else if( pObj1->x.iVal < pObj2->x.iVal ){
31842 				return -1;
31843 			}
31844 			return 0;
31845 		}
31846 	}
31847 	/* NOT REACHED */
31848 	SXUNUSED(iNest);
31849 	return 0;
31850 }
31851 /*
31852  * Perform an addition operation of two jx9_values.
31853  * The reason this function is implemented here rather than 'vm.c'
31854  * is that the '+' operator is overloaded.
31855  * That is, the '+' operator is used for arithmetic operation and also
31856  * used for operation on arrays [i.e: union]. When used with an array
31857  * The + operator returns the right-hand array appended to the left-hand array.
31858  * For keys that exist in both arrays, the elements from the left-hand array
31859  * will be used, and the matching elements from the right-hand array will
31860  * be ignored.
31861  * This function take care of handling all the scenarios.
31862  */
jx9MemObjAdd(jx9_value * pObj1,jx9_value * pObj2,int bAddStore)31863 JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore)
31864 {
31865 	if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
31866 			/* Arithemtic operation */
31867 			jx9MemObjToNumeric(pObj1);
31868 			jx9MemObjToNumeric(pObj2);
31869 			if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
31870 				/* Floating point arithmetic */
31871 				jx9_real a, b;
31872 				if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
31873 					jx9MemObjToReal(pObj1);
31874 				}
31875 				if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
31876 					jx9MemObjToReal(pObj2);
31877 				}
31878 				a = pObj1->x.rVal;
31879 				b = pObj2->x.rVal;
31880 				pObj1->x.rVal = a+b;
31881 				MemObjSetType(pObj1, MEMOBJ_REAL);
31882 				/* Try to get an integer representation also */
31883 				MemObjTryIntger(&(*pObj1));
31884 			}else{
31885 				/* Integer arithmetic */
31886 				sxi64 a, b;
31887 				a = pObj1->x.iVal;
31888 				b = pObj2->x.iVal;
31889 				pObj1->x.iVal = a+b;
31890 				MemObjSetType(pObj1, MEMOBJ_INT);
31891 			}
31892 	}else{
31893 		if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
31894 			jx9_hashmap *pMap;
31895 			sxi32 rc;
31896 			if( bAddStore ){
31897 				/* Do not duplicate the hashmap, use the left one since its an add&store operation.
31898 				 */
31899 				if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
31900 					/* Force a hashmap cast */
31901 					rc = jx9MemObjToHashmap(pObj1);
31902 					if( rc != SXRET_OK ){
31903 						jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
31904 						return rc;
31905 					}
31906 				}
31907 				/* Point to the structure that describe the hashmap */
31908 				pMap = (jx9_hashmap *)pObj1->x.pOther;
31909 			}else{
31910 				/* Create a new hashmap */
31911 				pMap = jx9NewHashmap(pObj1->pVm, 0, 0);
31912 				if( pMap == 0){
31913 					jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
31914 					return SXERR_MEM;
31915 				}
31916 			}
31917 			if( !bAddStore ){
31918 				if(pObj1->iFlags & MEMOBJ_HASHMAP ){
31919 					/* Perform a hashmap duplication */
31920 					jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap);
31921 				}else{
31922 					if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
31923 						/* Simple insertion */
31924 						jx9HashmapInsert(pMap, 0, pObj1);
31925 					}
31926 				}
31927 			}
31928 			/* Perform the union */
31929 			if(pObj2->iFlags & MEMOBJ_HASHMAP ){
31930 				jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther);
31931 			}else{
31932 				if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
31933 					/* Simple insertion */
31934 					jx9HashmapInsert(pMap, 0, pObj2);
31935 				}
31936 			}
31937 			/* Reflect the change */
31938 			if( pObj1->iFlags & MEMOBJ_STRING ){
31939 				SyBlobRelease(&pObj1->sBlob);
31940 			}
31941 			pObj1->x.pOther = pMap;
31942 			MemObjSetType(pObj1, MEMOBJ_HASHMAP);
31943 		}
31944 	}
31945 	return SXRET_OK;
31946 }
31947 /*
31948  * Return a printable representation of the type of a given
31949  * jx9_value.
31950  */
jx9MemObjTypeDump(jx9_value * pVal)31951 JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal)
31952 {
31953 	const char *zType = "";
31954 	if( pVal->iFlags & MEMOBJ_NULL ){
31955 		zType = "null";
31956 	}else if( pVal->iFlags & MEMOBJ_INT ){
31957 		zType = "int";
31958 	}else if( pVal->iFlags & MEMOBJ_REAL ){
31959 		zType = "float";
31960 	}else if( pVal->iFlags & MEMOBJ_STRING ){
31961 		zType = "string";
31962 	}else if( pVal->iFlags & MEMOBJ_BOOL ){
31963 		zType = "bool";
31964 	}else if( pVal->iFlags & MEMOBJ_HASHMAP ){
31965 		jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther;
31966 		if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
31967 			zType = "JSON Object";
31968 		}else{
31969 			zType = "JSON Array";
31970 		}
31971 	}else if( pVal->iFlags & MEMOBJ_RES ){
31972 		zType = "resource";
31973 	}
31974 	return zType;
31975 }
31976 /*
31977  * Dump a jx9_value [i.e: get a printable representation of it's type and contents.].
31978  * Store the dump in the given blob.
31979  */
jx9MemObjDump(SyBlob * pOut,jx9_value * pObj)31980 JX9_PRIVATE sxi32 jx9MemObjDump(
31981 	SyBlob *pOut,      /* Store the dump here */
31982 	jx9_value *pObj   /* Dump this */
31983 	)
31984 {
31985 	sxi32 rc = SXRET_OK;
31986 	const char *zType;
31987 	/* Get value type first */
31988 	zType = jx9MemObjTypeDump(pObj);
31989 	SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
31990 	if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
31991 		SyBlobAppend(&(*pOut), "(", sizeof(char));
31992 		if( pObj->iFlags & MEMOBJ_HASHMAP ){
31993 			jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
31994 			SyBlobFormat(pOut,"%u ",pMap->nEntry);
31995 			/* Dump hashmap entries */
31996 			rc = jx9JsonSerialize(pObj,pOut);
31997 		}else{
31998 			SyBlob *pContents = &pObj->sBlob;
31999 			/* Get a printable representation of the contents */
32000 			if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
32001 				MemObjStringValue(&(*pOut), &(*pObj));
32002 			}else{
32003 				/* Append length first */
32004 				SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob));
32005 				if( SyBlobLength(pContents) > 0 ){
32006 					SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents));
32007 				}
32008 				SyBlobAppend(&(*pOut), "'", sizeof(char));
32009 			}
32010 		}
32011 		SyBlobAppend(&(*pOut), ")", sizeof(char));
32012 	}
32013 #ifdef __WINNT__
32014 	SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1);
32015 #else
32016 	SyBlobAppend(&(*pOut), "\n", sizeof(char));
32017 #endif
32018 	return rc;
32019 }
32020 
32021 /* jx9_parse.c */
32022 /*
32023  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
32024  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
32025  * Version 1.7.2
32026  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
32027  * please contact Symisc Systems via:
32028  *       legal@symisc.net
32029  *       licensing@symisc.net
32030  *       contact@symisc.net
32031  * or visit:
32032  *      http://jx9.symisc.net/
32033  */
32034  /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */
32035 #ifndef JX9_AMALGAMATION
32036 #include "jx9Int.h"
32037 #endif
32038 /* Expression parser for the Jx9 programming language */
32039 /* Operators associativity */
32040 #define EXPR_OP_ASSOC_LEFT   0x01 /* Left associative operator */
32041 #define EXPR_OP_ASSOC_RIGHT  0x02 /* Right associative operator */
32042 #define EXPR_OP_NON_ASSOC    0x04 /* Non-associative operator */
32043 /*
32044  * Operators table
32045  * This table is sorted by operators priority (highest to lowest) according
32046  * the JX9 language reference manual.
32047  * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators.
32048  * The operators precedence table have been improved dramatically so that you can do same
32049  * amazing things now such as array dereferencing, on the fly function call, anonymous function
32050  * as array values, object member access on instantiation and so on.
32051  * Refer to the following page for a full discussion on these improvements:
32052  * http://jx9.symisc.net/features.html
32053  */
32054 static const jx9_expr_op aOpTable[] = {
32055 	                              /* Postfix operators */
32056 	/* Precedence 2(Highest), left-associative */
32057 	{ {".", sizeof(char)}, EXPR_OP_DOT,     2, EXPR_OP_ASSOC_LEFT ,   JX9_OP_MEMBER },
32058 	{ {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX},
32059 	/* Precedence 3, non-associative  */
32060 	{ {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR},
32061 	{ {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR},
32062 	                              /* Unary operators */
32063 	/* Precedence 4, right-associative  */
32064 	{ {"-", sizeof(char)},                 EXPR_OP_UMINUS,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS },
32065 	{ {"+", sizeof(char)},                 EXPR_OP_UPLUS,     4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS },
32066 	{ {"~", sizeof(char)},                 EXPR_OP_BITNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT },
32067 	{ {"!", sizeof(char)},                 EXPR_OP_LOGNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT },
32068 	                             /* Cast operators */
32069 	{ {"(int)",    sizeof("(int)")-1   }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT  },
32070 	{ {"(bool)",   sizeof("(bool)")-1  }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL },
32071 	{ {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR  },
32072 	{ {"(float)",  sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL },
32073 	{ {"(array)",  sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY },   /* Not used, but reserved for future use */
32074 	{ {"(object)",  sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
32075 	                           /* Binary operators */
32076 	/* Precedence 7, left-associative */
32077 	{ {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL},
32078 	{ {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV},
32079 	{ {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD},
32080 	/* Precedence 8, left-associative */
32081 	{ {"+", sizeof(char)}, EXPR_OP_ADD, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_ADD},
32082 	{ {"-", sizeof(char)}, EXPR_OP_SUB, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_SUB},
32083 	{ {"..", sizeof(char)*2},EXPR_OP_DDOT, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_CAT},
32084 	/* Precedence 9, left-associative */
32085 	{ {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL},
32086 	{ {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR},
32087 	/* Precedence 10, non-associative */
32088 	{ {"<", sizeof(char)},    EXPR_OP_LT,  10, EXPR_OP_NON_ASSOC, JX9_OP_LT},
32089 	{ {">", sizeof(char)},    EXPR_OP_GT,  10, EXPR_OP_NON_ASSOC, JX9_OP_GT},
32090 	{ {"<=", sizeof(char)*2}, EXPR_OP_LE,  10, EXPR_OP_NON_ASSOC, JX9_OP_LE},
32091 	{ {">=", sizeof(char)*2}, EXPR_OP_GE,  10, EXPR_OP_NON_ASSOC, JX9_OP_GE},
32092 	{ {"<>", sizeof(char)*2}, EXPR_OP_NE,  10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
32093 	/* Precedence 11, non-associative */
32094 	{ {"==", sizeof(char)*2},  EXPR_OP_EQ,  11, EXPR_OP_NON_ASSOC, JX9_OP_EQ},
32095 	{ {"!=", sizeof(char)*2},  EXPR_OP_NE,  11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
32096 	{ {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ},
32097 	{ {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE},
32098 		/* Precedence 12, left-associative */
32099 	{ {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT,   JX9_OP_BAND},
32100 	                         /* Binary operators */
32101 	/* Precedence 13, left-associative */
32102 	{ {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR},
32103 	/* Precedence 14, left-associative */
32104 	{ {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR},
32105 	/* Precedence 15, left-associative */
32106 	{ {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND},
32107 	/* Precedence 16, left-associative */
32108 	{ {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR},
32109 	                      /* Ternary operator */
32110 	/* Precedence 17, left-associative */
32111     { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0},
32112 	                     /* Combined binary operators */
32113 	/* Precedence 18, right-associative */
32114 	{ {"=", sizeof(char)},     EXPR_OP_ASSIGN,     18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE},
32115 	{ {"+=", sizeof(char)*2},  EXPR_OP_ADD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE },
32116 	{ {"-=", sizeof(char)*2},  EXPR_OP_SUB_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE },
32117 	{ {".=", sizeof(char)*2},  EXPR_OP_DOT_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE },
32118 	{ {"*=", sizeof(char)*2},  EXPR_OP_MUL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE },
32119 	{ {"/=", sizeof(char)*2},  EXPR_OP_DIV_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE },
32120 	{ {"%=", sizeof(char)*2},  EXPR_OP_MOD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE },
32121 	{ {"&=", sizeof(char)*2},  EXPR_OP_AND_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE },
32122 	{ {"|=", sizeof(char)*2},  EXPR_OP_OR_ASSIGN,  18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE  },
32123 	{ {"^=", sizeof(char)*2},  EXPR_OP_XOR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE },
32124 	{ {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE },
32125 	{ {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE },
32126 		/* Precedence 22, left-associative [Lowest operator] */
32127 	{ {",", sizeof(char)},  EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */
32128 };
32129 /* Function call operator need special handling */
32130 static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL};
32131 /*
32132  * Check if the given token is a potential operator or not.
32133  * This function is called by the lexer each time it extract a token that may
32134  * look like an operator.
32135  * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success.
32136  * Otherwise NULL.
32137  * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
32138  * a binary minus or unary minus.]
32139  */
jx9ExprExtractOperator(SyString * pStr,SyToken * pLast)32140 JX9_PRIVATE const jx9_expr_op *  jx9ExprExtractOperator(SyString *pStr, SyToken *pLast)
32141 {
32142 	sxu32 n = 0;
32143 	sxi32 rc;
32144 	/* Do a linear lookup on the operators table */
32145 	for(;;){
32146 		if( n >= SX_ARRAYSIZE(aOpTable) ){
32147 			break;
32148 		}
32149 		rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);
32150 		if( rc == 0 ){
32151 			if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){
32152 				if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){
32153 					/* JSON Array not subscripting, return NULL  */
32154 					return 0;
32155 				}
32156 				/* There is no ambiguity here, simply return the first operator seen */
32157 				return &aOpTable[n];
32158 			}
32159 			/* Handle ambiguity */
32160 			if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){
32161 				/* Unary opertors have prcedence here over binary operators */
32162 				return &aOpTable[n];
32163 			}
32164 			if( pLast->nType & JX9_TK_OP ){
32165 				const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData;
32166 				/* Ticket 1433-31: Handle the '++', '--' operators case */
32167 				if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){
32168 					/* Unary opertors have prcedence here over binary operators */
32169 					return &aOpTable[n];
32170 				}
32171 
32172 			}
32173 		}
32174 		++n; /* Next operator in the table */
32175 	}
32176 	/* No such operator */
32177 	return 0;
32178 }
32179 /*
32180  * Delimit a set of token stream.
32181  * This function take care of handling the nesting level and stops when it hit
32182  * the end of the input or the ending token is found and the nesting level is zero.
32183  */
jx9DelimitNestedTokens(SyToken * pIn,SyToken * pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken ** ppEnd)32184 JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd)
32185 {
32186 	SyToken *pCur = pIn;
32187 	sxi32 iNest = 1;
32188 	for(;;){
32189 		if( pCur >= pEnd ){
32190 			break;
32191 		}
32192 		if( pCur->nType & nTokStart ){
32193 			/* Increment nesting level */
32194 			iNest++;
32195 		}else if( pCur->nType & nTokEnd ){
32196 			/* Decrement nesting level */
32197 			iNest--;
32198 			if( iNest <= 0 ){
32199 				break;
32200 			}
32201 		}
32202 		/* Advance cursor */
32203 		pCur++;
32204 	}
32205 	/* Point to the end of the chunk */
32206 	*ppEnd = pCur;
32207 }
32208 /*
32209  * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise.
32210  * Note on reserved keywords.
32211  *  According to the JX9 language reference manual:
32212  *   These words have special meaning in JX9. Some of them represent things which look like
32213  *   functions, some look like constants, and so on--but they're not, really: they are language
32214  *   constructs. You cannot use any of the following words as constants, object names, function
32215  *   or method names. Using them as variable names is generally OK, but could lead to confusion.
32216  */
jx9IsLangConstruct(sxu32 nKeyID)32217 JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID)
32218 {
32219 	if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE
32220 		|| nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){
32221 			return TRUE;
32222 	}
32223 	/* Not a language construct */
32224 	return FALSE;
32225 }
32226 /*
32227  * Point to the next expression that should be evaluated shortly.
32228  * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting
32229  * level is zero.
32230  */
jx9GetNextExpr(SyToken * pStart,SyToken * pEnd,SyToken ** ppNext)32231 JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext)
32232 {
32233 	SyToken *pCur = pStart;
32234 	sxi32 iNest = 0;
32235 	if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){
32236 		/* Last expression */
32237 		return SXERR_EOF;
32238 	}
32239 	while( pCur < pEnd ){
32240 		if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){
32241 			break;
32242 		}
32243 		if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){
32244 			iNest++;
32245 		}else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){
32246 			iNest--;
32247 		}
32248 		pCur++;
32249 	}
32250 	*ppNext = pCur;
32251 	return SXRET_OK;
32252 }
32253 /*
32254  * Collect and assemble tokens holding annonymous functions/closure body.
32255  * When errors, JX9 take care of generating the appropriate error message.
32256  * Note on annonymous functions.
32257  *  According to the JX9 language reference manual:
32258  *  Anonymous functions, also known as closures, allow the creation of functions
32259  *  which have no specified name. They are most useful as the value of callback
32260  *  parameters, but they have many other uses.
32261  *  Closures may also inherit variables from the parent scope. Any such variables
32262  *  must be declared in the function header. Inheriting variables from the parent
32263  *  scope is not the same as using global variables. Global variables exist in the global scope
32264  *  which is the same no matter what function is executing. The parent scope of a closure is the
32265  *  function in which the closure was declared (not necessarily the function it was called from).
32266  *
32267  * Some example:
32268  *  $greet = function($name)
32269  * {
32270  *   printf("Hello %s\r\n", $name);
32271  * };
32272  *  $greet('World');
32273  *  $greet('JX9');
32274  *
32275  * $double = function($a) {
32276  *   return $a * 2;
32277  * };
32278  * // This is our range of numbers
32279  * $numbers = range(1, 5);
32280  * // Use the Annonymous function as a callback here to
32281  * // double the size of each element in our
32282  * // range
32283  * $new_numbers = array_map($double, $numbers);
32284  * print implode(' ', $new_numbers);
32285  */
ExprAssembleAnnon(jx9_gen_state * pGen,SyToken ** ppCur,SyToken * pEnd)32286 static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd)
32287 {
32288 	SyToken *pIn = *ppCur;
32289 	sxu32 nLine;
32290 	sxi32 rc;
32291 	/* Jump the 'function' keyword */
32292 	nLine = pIn->nLine;
32293 	pIn++;
32294 	if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){
32295 		pIn++;
32296 	}
32297 	if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){
32298 		/* Syntax error */
32299 		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function");
32300 		if( rc != SXERR_ABORT ){
32301 			rc = SXERR_SYNTAX;
32302 		}
32303 		goto Synchronize;
32304 	}
32305 	pIn++; /* Jump the leading parenthesis '(' */
32306 	jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn);
32307 	if( pIn >= pEnd || &pIn[1] >= pEnd ){
32308 		/* Syntax error */
32309 		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function");
32310 		if( rc != SXERR_ABORT ){
32311 			rc = SXERR_SYNTAX;
32312 		}
32313 		goto Synchronize;
32314 	}
32315 	pIn++; /* Jump the trailing parenthesis */
32316 	if( pIn->nType & JX9_TK_OCB /*'{'*/ ){
32317 		pIn++; /* Jump the leading curly '{' */
32318 		jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn);
32319 		if( pIn < pEnd ){
32320 			pIn++;
32321 		}
32322 	}else{
32323 		/* Syntax error */
32324 		rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'");
32325 		if( rc == SXERR_ABORT ){
32326 			return SXERR_ABORT;
32327 		}
32328 	}
32329 	rc = SXRET_OK;
32330 Synchronize:
32331 	/* Synchronize pointers */
32332 	*ppCur = pIn;
32333 	return rc;
32334 }
32335 /*
32336  * Make sure we are dealing with a valid expression tree.
32337  * This function check for balanced parenthesis, braces, brackets and so on.
32338  * When errors, JX9 take care of generating the appropriate error message.
32339  * Return SXRET_OK on success. Any other return value indicates syntax error.
32340  */
ExprVerifyNodes(jx9_gen_state * pGen,jx9_expr_node ** apNode,sxi32 nNode)32341 static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode)
32342 {
32343 	sxi32 iParen, iSquare, iBraces;
32344 	sxi32 i, rc;
32345 
32346 	if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){
32347 		/* Fix and mark as an unary not binary plus/minus operator */
32348 		apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0);
32349 		apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
32350 	}
32351 	iParen = iSquare = iBraces = 0;
32352 	for( i = 0 ; i < nNode ; ++i ){
32353 		if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){
32354 			if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ||
32355 				(apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){
32356 					/* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */
32357 					if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){
32358 						/* We are dealing with a postfix [i.e: function call]  operator
32359 						 * not a simple left parenthesis. Mark the node.
32360 						 */
32361 						apNode[i]->pStart->nType |= JX9_TK_OP;
32362 						apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
32363 						apNode[i]->pOp = &sFCallOp;
32364 					}
32365 			}
32366 			iParen++;
32367 		}else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){
32368 			if( iParen <= 0 ){
32369 				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
32370 				if( rc != SXERR_ABORT ){
32371 					rc = SXERR_SYNTAX;
32372 				}
32373 				return rc;
32374 			}
32375 			iParen--;
32376 		}else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){
32377 			iSquare++;
32378 		}else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){
32379 			if( iSquare <= 0 ){
32380 				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
32381 				if( rc != SXERR_ABORT ){
32382 					rc = SXERR_SYNTAX;
32383 				}
32384 				return rc;
32385 			}
32386 			iSquare--;
32387 		}else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){
32388 			iBraces++;
32389 		}else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){
32390 			if( iBraces <= 0 ){
32391 				rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
32392 				if( rc != SXERR_ABORT ){
32393 					rc = SXERR_SYNTAX;
32394 				}
32395 				return rc;
32396 			}
32397 			iBraces--;
32398 		}else if( apNode[i]->pStart->nType & JX9_TK_OP ){
32399 			const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp;
32400 			if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){
32401 				if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){
32402 					sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
32403 					sxu32 n = 0;
32404 					if( pOp->iOp == EXPR_OP_UPLUS ){
32405 						iExprOp = EXPR_OP_ADD; /* Binary plus */
32406 					}
32407 					/*
32408 					 * TICKET 1433-013: This is a fix around an obscure bug when the user uses
32409 					 * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..].
32410 					 */
32411 					while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){
32412 						++n;
32413 					}
32414 					pOp = &aOpTable[n];
32415 					/* Mark as binary '+' or '-', not an unary */
32416 					apNode[i]->pOp = pOp;
32417 					apNode[i]->pStart->pUserData = (void *)pOp;
32418 				}
32419 			}
32420 		}
32421 	}
32422 	if( iParen != 0 || iSquare != 0 || iBraces != 0){
32423 		rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'");
32424 		if( rc != SXERR_ABORT ){
32425 			rc = SXERR_SYNTAX;
32426 		}
32427 		return rc;
32428 	}
32429 	return SXRET_OK;
32430 }
32431 /*
32432  * Extract a single expression node from the input.
32433  * On success store the freshly extractd node in ppNode.
32434  * When errors, JX9 take care of generating the appropriate error message.
32435  * An expression node can be a variable [i.e: $var], an operator [i.e: ++]
32436  * an annonymous function [i.e: function(){ return "Hello"; }, a double/single
32437  * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path
32438  * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on.
32439  */
ExprExtractNode(jx9_gen_state * pGen,jx9_expr_node ** ppNode)32440 static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode)
32441 {
32442 	jx9_expr_node *pNode;
32443 	SyToken *pCur;
32444 	sxi32 rc;
32445 	/* Allocate a new node */
32446 	pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node));
32447 	if( pNode == 0 ){
32448 		/* If the supplied memory subsystem is so sick that we are unable to allocate
32449 		 * a tiny chunk of memory, there is no much we can do here.
32450 		 */
32451 		return SXERR_MEM;
32452 	}
32453 	/* Zero the structure */
32454 	SyZero(pNode, sizeof(jx9_expr_node));
32455 	SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **));
32456 	/* Point to the head of the token stream */
32457 	pCur = pNode->pStart = pGen->pIn;
32458 	/* Start collecting tokens */
32459 	if( pCur->nType & JX9_TK_OP ){
32460 		/* Point to the instance that describe this operator */
32461 		pNode->pOp = (const jx9_expr_op *)pCur->pUserData;
32462 		/* Advance the stream cursor */
32463 		pCur++;
32464 	}else if( pCur->nType & JX9_TK_DOLLAR ){
32465 		/* Isolate variable */
32466 		pCur++; /* Jump the dollar sign */
32467 		if( pCur >= pGen->pEnd ){
32468 			/* Syntax error */
32469 			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name");
32470 			if( rc != SXERR_ABORT ){
32471 				rc = SXERR_SYNTAX;
32472 			}
32473 			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32474 			return rc;
32475 		}
32476 		pCur++; /* Jump the variable name */
32477 		pNode->xCode = jx9CompileVariable;
32478 	}else if( pCur->nType & JX9_TK_OCB /* '{' */ ){
32479 		/* JSON Object, assemble tokens */
32480 		pCur++;
32481 		jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur);
32482 		if( pCur < pGen->pEnd ){
32483 			pCur++;
32484 		}else{
32485 			/* Syntax error */
32486 			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'");
32487 			if( rc != SXERR_ABORT ){
32488 				rc = SXERR_SYNTAX;
32489 			}
32490 			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32491 			return rc;
32492 		}
32493 		pNode->xCode = jx9CompileJsonObject;
32494 	}else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){
32495 		/* JSON Array, assemble tokens */
32496 		pCur++;
32497 		jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur);
32498 		if( pCur < pGen->pEnd ){
32499 			pCur++;
32500 		}else{
32501 			/* Syntax error */
32502 			rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'");
32503 			if( rc != SXERR_ABORT ){
32504 				rc = SXERR_SYNTAX;
32505 			}
32506 			SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32507 			return rc;
32508 		}
32509 		pNode->xCode = jx9CompileJsonArray;
32510 	}else if( pCur->nType & JX9_TK_KEYWORD ){
32511 		 int nKeyword = SX_PTR_TO_INT(pCur->pUserData);
32512 		 if( nKeyword == JX9_TKWRD_FUNCTION ){
32513 			 /* Annonymous function */
32514 			  if( &pCur[1] >= pGen->pEnd ){
32515 				 /* Assume a literal */
32516 				pCur++;
32517 				pNode->xCode = jx9CompileLiteral;
32518 			 }else{
32519 				 /* Assemble annonymous functions body */
32520 				 rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd);
32521 				 if( rc != SXRET_OK ){
32522 					 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32523 					 return rc;
32524 				 }
32525 				 pNode->xCode = jx9CompileAnnonFunc;
32526 			  }
32527 		 }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){
32528 			 /* Language constructs [i.e: print,die...] require special handling */
32529 			 jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur);
32530 			 pNode->xCode = jx9CompileLangConstruct;
32531 		 }else{
32532 			 /* Assume a literal */
32533 			 pCur++;
32534 			 pNode->xCode = jx9CompileLiteral;
32535 		 }
32536 	 }else if( pCur->nType & (JX9_TK_ID) ){
32537 		 /* Constants, function name, namespace path, object name... */
32538 		 pCur++;
32539 		 pNode->xCode = jx9CompileLiteral;
32540 	 }else{
32541 		 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 ){
32542 			 /* Point to the code generator routine */
32543 			 pNode->xCode = jx9GetNodeHandler(pCur->nType);
32544 			 if( pNode->xCode == 0 ){
32545 				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
32546 				 if( rc != SXERR_ABORT ){
32547 					 rc = SXERR_SYNTAX;
32548 				 }
32549 				 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32550 				 return rc;
32551 			 }
32552 		 }
32553 		/* Advance the stream cursor */
32554 		pCur++;
32555 	 }
32556 	/* Point to the end of the token stream */
32557 	pNode->pEnd = pCur;
32558 	/* Save the node for later processing */
32559 	*ppNode = pNode;
32560 	/* Synchronize cursors */
32561 	pGen->pIn = pCur;
32562 	return SXRET_OK;
32563 }
32564 /*
32565  * Free an expression tree.
32566  */
ExprFreeTree(jx9_gen_state * pGen,jx9_expr_node * pNode)32567 static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode)
32568 {
32569 	if( pNode->pLeft ){
32570 		/* Release the left tree */
32571 		ExprFreeTree(&(*pGen), pNode->pLeft);
32572 	}
32573 	if( pNode->pRight ){
32574 		/* Release the right tree */
32575 		ExprFreeTree(&(*pGen), pNode->pRight);
32576 	}
32577 	if( pNode->pCond ){
32578 		/* Release the conditional tree used by the ternary operator */
32579 		ExprFreeTree(&(*pGen), pNode->pCond);
32580 	}
32581 	if( SySetUsed(&pNode->aNodeArgs) > 0 ){
32582 		jx9_expr_node **apArg;
32583 		sxu32 n;
32584 		/* Release node arguments */
32585 		apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
32586 		for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){
32587 			ExprFreeTree(&(*pGen), apArg[n]);
32588 		}
32589 		SySetRelease(&pNode->aNodeArgs);
32590 	}
32591 	/* Finally, release this node */
32592 	SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
32593 }
32594 /*
32595  * Free an expression tree.
32596  * This function is a wrapper around ExprFreeTree() defined above.
32597  */
jx9ExprFreeTree(jx9_gen_state * pGen,SySet * pNodeSet)32598 JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet)
32599 {
32600 	jx9_expr_node **apNode;
32601 	sxu32 n;
32602 	apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet);
32603 	for( n = 0  ; n < SySetUsed(pNodeSet) ; ++n ){
32604 		if( apNode[n] ){
32605 			ExprFreeTree(&(*pGen), apNode[n]);
32606 		}
32607 	}
32608 	return SXRET_OK;
32609 }
32610 /*
32611  * Check if the given node is a modifialbe l/r-value.
32612  * Return TRUE if modifiable.FALSE otherwise.
32613  */
ExprIsModifiableValue(jx9_expr_node * pNode)32614 static int ExprIsModifiableValue(jx9_expr_node *pNode)
32615 {
32616 	sxi32 iExprOp;
32617 	if( pNode->pOp == 0 ){
32618 		return pNode->xCode == jx9CompileVariable ? TRUE : FALSE;
32619 	}
32620 	iExprOp = pNode->pOp->iOp;
32621 	if( iExprOp == EXPR_OP_DOT /*'.' */  ){
32622 			return TRUE;
32623 	}
32624 	if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){
32625 		if( pNode->pLeft->pOp ) {
32626 			if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){
32627 				return FALSE;
32628 			}
32629 		}else if( pNode->pLeft->xCode != jx9CompileVariable ){
32630 			return FALSE;
32631 		}
32632 		return TRUE;
32633 	}
32634 	/* Not a modifiable l or r-value */
32635 	return FALSE;
32636 }
32637 /* Forward declaration */
32638 static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken);
32639 /* Macro to check if the given node is a terminal */
32640 #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
32641 /*
32642  * Buid an expression tree for each given function argument.
32643  * When errors, JX9 take care of generating the appropriate error message.
32644  */
ExprProcessFuncArguments(jx9_gen_state * pGen,jx9_expr_node * pOp,jx9_expr_node ** apNode,sxi32 nToken)32645 static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken)
32646 {
32647 	sxi32 iNest, iCur, iNode;
32648 	sxi32 rc;
32649 	/* Process function arguments from left to right */
32650 	iCur = 0;
32651 	for(;;){
32652 		if( iCur >= nToken ){
32653 			/* No more arguments to process */
32654 			break;
32655 		}
32656 		iNode = iCur;
32657 		iNest = 0;
32658 		while( iCur < nToken ){
32659 			if( apNode[iCur] ){
32660 				if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){
32661 					break;
32662 				}else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){
32663 					iNest++;
32664 				}else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){
32665 					iNest--;
32666 				}
32667 			}
32668 			iCur++;
32669 		}
32670 		if( iCur > iNode ){
32671 			ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode);
32672 			if( apNode[iNode] ){
32673 				/* Put a pointer to the root of the tree in the arguments set */
32674 				SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
32675 			}else{
32676 				/* Empty function argument */
32677 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
32678 				if( rc != SXERR_ABORT ){
32679 					rc = SXERR_SYNTAX;
32680 				}
32681 				return rc;
32682 			}
32683 		}else{
32684 			rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
32685 			if( rc != SXERR_ABORT ){
32686 				rc = SXERR_SYNTAX;
32687 			}
32688 			return rc;
32689 		}
32690 		/* Jump trailing comma */
32691 		if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){
32692 			iCur++;
32693 			if( iCur >= nToken ){
32694 				/* missing function argument */
32695 				rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
32696 				if( rc != SXERR_ABORT ){
32697 					rc = SXERR_SYNTAX;
32698 				}
32699 				return rc;
32700 			}
32701 		}
32702 	}
32703 	return SXRET_OK;
32704 }
32705 /*
32706   * Create an expression tree from an array of tokens.
32707   * If successful, the root of the tree is stored in apNode[0].
32708   * When errors, JX9 take care of generating the appropriate error message.
32709   */
ExprMakeTree(jx9_gen_state * pGen,jx9_expr_node ** apNode,sxi32 nToken)32710  static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken)
32711  {
32712 	 sxi32 i, iLeft, iRight;
32713 	 jx9_expr_node *pNode;
32714 	 sxi32 iCur;
32715 	 sxi32 rc;
32716 	 if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){
32717 		 /* TICKET 1433-17: self evaluating node */
32718 		 return SXRET_OK;
32719 	 }
32720 	 /* Process expressions enclosed in parenthesis first */
32721 	 for( iCur =  0 ; iCur < nToken ; ++iCur ){
32722 		 sxi32 iNest;
32723 		 /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
32724 		  * since the LPAREN token can also be an operator [i.e: Function call].
32725 		  */
32726 		 if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){
32727 			 continue;
32728 		 }
32729 		 iNest = 1;
32730 		 iLeft = iCur;
32731 		 /* Find the closing parenthesis */
32732 		 iCur++;
32733 		 while( iCur < nToken ){
32734 			 if( apNode[iCur] ){
32735 				 if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){
32736 					 /* Decrement nesting level */
32737 					 iNest--;
32738 					 if( iNest <= 0 ){
32739 						 break;
32740 					 }
32741 				 }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){
32742 					 /* Increment nesting level */
32743 					 iNest++;
32744 				 }
32745 			 }
32746 			 iCur++;
32747 		 }
32748 		 if( iCur - iLeft > 1 ){
32749 			 /* Recurse and process this expression */
32750 			 rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
32751 			 if( rc != SXRET_OK ){
32752 				 return rc;
32753 			 }
32754 		 }
32755 		 /* Free the left and right nodes */
32756 		 ExprFreeTree(&(*pGen), apNode[iLeft]);
32757 		 ExprFreeTree(&(*pGen), apNode[iCur]);
32758 		 apNode[iLeft] = 0;
32759 		 apNode[iCur] = 0;
32760 	 }
32761 	 /* Handle postfix [i.e: function call, member access] operators with precedence 2 */
32762 	 iLeft = -1;
32763 	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32764 		 if( apNode[iCur] == 0 ){
32765 			 continue;
32766 		 }
32767 		 pNode = apNode[iCur];
32768 		 if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0  ){
32769 			 if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){
32770 				 /* Collect function arguments */
32771 				 sxi32 iPtr = 0;
32772 				 sxi32 nFuncTok = 0;
32773 				 while( nFuncTok + iCur < nToken ){
32774 					 if( apNode[nFuncTok+iCur] ){
32775 						 if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){
32776 							 iPtr++;
32777 						 }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){
32778 							 iPtr--;
32779 							 if( iPtr <= 0 ){
32780 								 break;
32781 							 }
32782 						 }
32783 					 }
32784 					 nFuncTok++;
32785 				 }
32786 				 if( nFuncTok + iCur >= nToken ){
32787 					 /* Syntax error */
32788 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
32789 					 if( rc != SXERR_ABORT ){
32790 						 rc = SXERR_SYNTAX;
32791 					 }
32792 					 return rc;
32793 				 }
32794 				 if(  iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){
32795 					 /* Syntax error */
32796 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
32797 					 if( rc != SXERR_ABORT ){
32798 						 rc = SXERR_SYNTAX;
32799 					 }
32800 					 return rc;
32801 				 }
32802 				 if( nFuncTok > 1 ){
32803 					 /* Process function arguments */
32804 					 rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1);
32805 					 if( rc != SXRET_OK ){
32806 						 return rc;
32807 					 }
32808 				 }
32809 				 /* Link the node to the tree */
32810 				 pNode->pLeft = apNode[iLeft];
32811 				 apNode[iLeft] = 0;
32812 				 for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){
32813 					 apNode[iCur+iPtr] = 0;
32814 				 }
32815 			 }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){
32816 				 /* Subscripting */
32817 				 sxi32 iArrTok = iCur + 1;
32818 				 sxi32 iNest = 1;
32819 				 if(  iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){
32820 					 /* Collect index tokens */
32821 				    while( iArrTok < nToken ){
32822 					 if( apNode[iArrTok] ){
32823 						 if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){
32824 							 /* Increment nesting level */
32825 							 iNest++;
32826 						 }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){
32827 							 /* Decrement nesting level */
32828 							 iNest--;
32829 							 if( iNest <= 0 ){
32830 								 break;
32831 							 }
32832 						 }
32833 					 }
32834 					 ++iArrTok;
32835 				   }
32836 				   if( iArrTok > iCur + 1 ){
32837 					 /* Recurse and process this expression */
32838 					 rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1);
32839 					 if( rc != SXRET_OK ){
32840 						 return rc;
32841 					 }
32842 					 /* Link the node to it's index */
32843 					 SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]);
32844 				   }
32845 				   /* Link the node to the tree */
32846 				   pNode->pLeft = apNode[iLeft];
32847 				   pNode->pRight = 0;
32848 				   apNode[iLeft] = 0;
32849 				   for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){
32850 					 apNode[iNest] = 0;
32851 				  }
32852 				 }
32853 			 }else{
32854 				 /* Member access operators [i.e: '.' ] */
32855 				 iRight = iCur + 1;
32856 				 while( iRight < nToken && apNode[iRight] == 0 ){
32857 					 iRight++;
32858 				 }
32859 				 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
32860 					 /* Syntax error */
32861 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
32862 					 if( rc != SXERR_ABORT ){
32863 						 rc = SXERR_SYNTAX;
32864 					 }
32865 					 return rc;
32866 				 }
32867 				 /* Link the node to the tree */
32868 				 pNode->pLeft = apNode[iLeft];
32869 				 if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){
32870 					 /* Syntax error */
32871 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
32872 						 "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
32873 					 if( rc != SXERR_ABORT ){
32874 						 rc = SXERR_SYNTAX;
32875 					 }
32876 					 return rc;
32877 				 }
32878 				 pNode->pRight = apNode[iRight];
32879 				 apNode[iLeft] = apNode[iRight] = 0;
32880 			 }
32881 		 }
32882 		 iLeft = iCur;
32883 	 }
32884 	  /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */
32885 	 iLeft = -1;
32886 	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32887 		 if( apNode[iCur] == 0 ){
32888 			 continue;
32889 		 }
32890 		 pNode = apNode[iCur];
32891 		 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
32892 			 if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
32893 				 || apNode[iLeft]->xCode == jx9CompileVariable) ){
32894 					 /* Link the node to the tree */
32895 					 pNode->pLeft = apNode[iLeft];
32896 					 apNode[iLeft] = 0;
32897 			 }
32898 		  }
32899 		 iLeft = iCur;
32900 	  }
32901 	 iLeft = -1;
32902 	 for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
32903 		 if( apNode[iCur] == 0 ){
32904 			 continue;
32905 		 }
32906 		 pNode = apNode[iCur];
32907 		 if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
32908 			 if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable)
32909 				 || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){
32910 					 /* Syntax error */
32911 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
32912 					 if( rc != SXERR_ABORT ){
32913 						 rc = SXERR_SYNTAX;
32914 					 }
32915 					 return rc;
32916 			 }
32917 			 /* Link the node to the tree */
32918 			 pNode->pLeft = apNode[iLeft];
32919 			 apNode[iLeft] = 0;
32920 			 /* Mark as pre-increment/decrement node */
32921 			 pNode->iFlags |= EXPR_NODE_PRE_INCR;
32922 		  }
32923 		 iLeft = iCur;
32924 	 }
32925 	 /* Handle right associative unary and cast operators [i.e: !, (string), ~...]  with precedence 4 */
32926 	  iLeft = 0;
32927 	  for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
32928 		  if( apNode[iCur] ){
32929 			  pNode = apNode[iCur];
32930 			  if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){
32931 				  if( iLeft > 0 ){
32932 					  /* Link the node to the tree */
32933 					  pNode->pLeft = apNode[iLeft];
32934 					  apNode[iLeft] = 0;
32935 					  if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){
32936 						  if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){
32937 							   /* Syntax error */
32938 							  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
32939 							  if( rc != SXERR_ABORT ){
32940 								  rc = SXERR_SYNTAX;
32941 							  }
32942 							  return rc;
32943 						  }
32944 					  }
32945 				  }else{
32946 					  /* Syntax error */
32947 					  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
32948 					  if( rc != SXERR_ABORT ){
32949 						  rc = SXERR_SYNTAX;
32950 					  }
32951 					  return rc;
32952 				  }
32953 			  }
32954 			  /* Save terminal position */
32955 			  iLeft = iCur;
32956 		  }
32957 	  }
32958 	 /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/
32959 	 for( i = 7 ; i < 17 ; i++ ){
32960 		 iLeft = -1;
32961 		 for( iCur = 0 ; iCur < nToken ; ++iCur ){
32962 			 if( apNode[iCur] == 0 ){
32963 				 continue;
32964 			 }
32965 			 pNode = apNode[iCur];
32966 			 if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){
32967 				 /* Get the right node */
32968 				 iRight = iCur + 1;
32969 				 while( iRight < nToken && apNode[iRight] == 0 ){
32970 					 iRight++;
32971 				 }
32972 				 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
32973 					 /* Syntax error */
32974 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
32975 					 if( rc != SXERR_ABORT ){
32976 						 rc = SXERR_SYNTAX;
32977 					 }
32978 					 return rc;
32979 				 }
32980 				 /* Link the node to the tree */
32981 				 pNode->pLeft = apNode[iLeft];
32982 				 pNode->pRight = apNode[iRight];
32983 				 apNode[iLeft] = apNode[iRight] = 0;
32984 			 }
32985 			 iLeft = iCur;
32986 		 }
32987 	 }
32988 	 /* Handle the ternary operator. (expr1) ? (expr2) : (expr3)
32989 	  * Note that we do not need a precedence loop here since
32990 	  * we are dealing with a single operator.
32991 	  */
32992 	  iLeft = -1;
32993 	  for( iCur = 0 ; iCur < nToken ; ++iCur ){
32994 		  if( apNode[iCur] == 0 ){
32995 			  continue;
32996 		  }
32997 		  pNode = apNode[iCur];
32998 		  if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){
32999 			  sxi32 iNest = 1;
33000 			  if( iLeft < 0 || !NODE_ISTERM(iLeft) ){
33001 				  /* Missing condition */
33002 				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
33003 				  if( rc != SXERR_ABORT ){
33004 					  rc = SXERR_SYNTAX;
33005 				  }
33006 				  return rc;
33007 			  }
33008 			  /* Get the right node */
33009 			  iRight = iCur + 1;
33010 			  while( iRight < nToken  ){
33011 				  if( apNode[iRight] ){
33012 					  if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){
33013 						  /* Increment nesting level */
33014 						  ++iNest;
33015 					  }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){
33016 						  /* Decrement nesting level */
33017 						  --iNest;
33018 						  if( iNest <= 0 ){
33019 							  break;
33020 						  }
33021 					  }
33022 				  }
33023 				  iRight++;
33024 			  }
33025 			  if( iRight > iCur + 1 ){
33026 				  /* Recurse and process the then expression */
33027 				  rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
33028 				  if( rc != SXRET_OK ){
33029 					  return rc;
33030 				  }
33031 				  /* Link the node to the tree */
33032 				  pNode->pLeft = apNode[iCur + 1];
33033 			  }else{
33034 				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
33035 				  if( rc != SXERR_ABORT ){
33036 					 rc = SXERR_SYNTAX;
33037 				 }
33038 				 return rc;
33039 			  }
33040 			  apNode[iCur + 1] = 0;
33041 			  if( iRight + 1 < nToken ){
33042 				  /* Recurse and process the else expression */
33043 				  rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
33044 				  if( rc != SXRET_OK ){
33045 					  return rc;
33046 				  }
33047 				  /* Link the node to the tree */
33048 				  pNode->pRight = apNode[iRight + 1];
33049 				  apNode[iRight + 1] =  apNode[iRight] = 0;
33050 			  }else{
33051 				  rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
33052 				  if( rc != SXERR_ABORT ){
33053 					 rc = SXERR_SYNTAX;
33054 				 }
33055 				 return rc;
33056 			  }
33057 			  /* Point to the condition */
33058 			  pNode->pCond  = apNode[iLeft];
33059 			  apNode[iLeft] = 0;
33060 			  break;
33061 		  }
33062 		  iLeft = iCur;
33063 	  }
33064 	 /* Process right associative binary operators [i.e: '=', '+=', '/=']
33065 	  * Note: All right associative binary operators have precedence 18
33066 	  * so there is no need for a precedence loop here.
33067 	  */
33068 	 iRight = -1;
33069 	 for( iCur = nToken -  1 ; iCur >= 0 ; iCur--){
33070 		 if( apNode[iCur] == 0 ){
33071 			 continue;
33072 		 }
33073 		 pNode = apNode[iCur];
33074 		 if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){
33075 			 /* Get the left node */
33076 			 iLeft = iCur - 1;
33077 			 while( iLeft >= 0 && apNode[iLeft] == 0 ){
33078 				 iLeft--;
33079 			 }
33080 			 if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
33081 				 /* Syntax error */
33082 				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
33083 				 if( rc != SXERR_ABORT ){
33084 					 rc = SXERR_SYNTAX;
33085 				 }
33086 				 return rc;
33087 			 }
33088 			 if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){
33089 				 if( pNode->pOp->iVmOp != JX9_OP_STORE  ){
33090 					 /* Left operand must be a modifiable l-value */
33091 					 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
33092 						 "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
33093 					 if( rc != SXERR_ABORT ){
33094 						 rc = SXERR_SYNTAX;
33095 					 }
33096 					 return rc;
33097 				 }
33098 			 }
33099 			 /* Link the node to the tree (Reverse) */
33100 			 pNode->pLeft = apNode[iRight];
33101 			 pNode->pRight = apNode[iLeft];
33102 			 apNode[iLeft] = apNode[iRight] = 0;
33103 		 }
33104 		 iRight = iCur;
33105 	 }
33106 	 /* Process the lowest precedence operator (22, comma) */
33107 	 iLeft = -1;
33108 	 for( iCur = 0 ; iCur < nToken ; ++iCur ){
33109 		 if( apNode[iCur] == 0 ){
33110 			 continue;
33111 		 }
33112 		 pNode = apNode[iCur];
33113 		 if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){
33114 			 /* Get the right node */
33115 			 iRight = iCur + 1;
33116 			 while( iRight < nToken && apNode[iRight] == 0 ){
33117 				 iRight++;
33118 			 }
33119 			 if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
33120 				 /* Syntax error */
33121 				 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
33122 				 if( rc != SXERR_ABORT ){
33123 					 rc = SXERR_SYNTAX;
33124 				 }
33125 				 return rc;
33126 			 }
33127 			 /* Link the node to the tree */
33128 			 pNode->pLeft = apNode[iLeft];
33129 			 pNode->pRight = apNode[iRight];
33130 			 apNode[iLeft] = apNode[iRight] = 0;
33131 		 }
33132 		 iLeft = iCur;
33133 	 }
33134 	 /* Point to the root of the expression tree */
33135 	 for( iCur = 1 ; iCur < nToken ; ++iCur ){
33136 		 if( apNode[iCur] ){
33137 			 if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){
33138 				 rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
33139 				  if( rc != SXERR_ABORT ){
33140 					  rc = SXERR_SYNTAX;
33141 				  }
33142 				  return rc;
33143 			 }
33144 			 apNode[0] = apNode[iCur];
33145 			 apNode[iCur] = 0;
33146 		 }
33147 	 }
33148 	 return SXRET_OK;
33149  }
33150  /*
33151   * Build an expression tree from the freshly extracted raw tokens.
33152   * If successful, the root of the tree is stored in ppRoot.
33153   * When errors, JX9 take care of generating the appropriate error message.
33154   * This is the public interface used by the most code generator routines.
33155   */
jx9ExprMakeTree(jx9_gen_state * pGen,SySet * pExprNode,jx9_expr_node ** ppRoot)33156 JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot)
33157 {
33158 	jx9_expr_node **apNode;
33159 	jx9_expr_node *pNode;
33160 	sxi32 rc;
33161 	/* Reset node container */
33162 	SySetReset(pExprNode);
33163 	pNode = 0; /* Prevent compiler warning */
33164 	/* Extract nodes one after one until we hit the end of the input */
33165 	while( pGen->pIn < pGen->pEnd ){
33166 		rc = ExprExtractNode(&(*pGen), &pNode);
33167 		if( rc != SXRET_OK ){
33168 			return rc;
33169 		}
33170 		/* Save the extracted node */
33171 		SySetPut(pExprNode, (const void *)&pNode);
33172 	}
33173 	if( SySetUsed(pExprNode) < 1 ){
33174 		/* Empty expression [i.e: A semi-colon;] */
33175 		*ppRoot = 0;
33176 		return SXRET_OK;
33177 	}
33178 	apNode = (jx9_expr_node **)SySetBasePtr(pExprNode);
33179 	/* Make sure we are dealing with valid nodes */
33180 	rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
33181 	if( rc != SXRET_OK ){
33182 		/* Don't worry about freeing memory, upper layer will
33183 		 * cleanup the mess left behind.
33184 		 */
33185 		*ppRoot = 0;
33186 		return rc;
33187 	}
33188 	/* Build the tree */
33189 	rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
33190 	if( rc != SXRET_OK ){
33191 		/* Something goes wrong [i.e: Syntax error] */
33192 		*ppRoot = 0;
33193 		return rc;
33194 	}
33195 	/* Point to the root of the tree */
33196 	*ppRoot = apNode[0];
33197 	return SXRET_OK;
33198 }
33199 
33200 /* jx9_vfs.c */
33201 /*
33202  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
33203  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
33204  * Version 1.7.2
33205  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
33206  * please contact Symisc Systems via:
33207  *       legal@symisc.net
33208  *       licensing@symisc.net
33209  *       contact@symisc.net
33210  * or visit:
33211  *      http://jx9.symisc.net/
33212  */
33213  /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
33214 #ifndef JX9_AMALGAMATION
33215 #include "jx9Int.h"
33216 #endif
33217 /*
33218  * This file implement a virtual file systems (VFS) for the JX9 engine.
33219  */
33220 /*
33221  * Given a string containing the path of a file or directory, this function
33222  * return the parent directory's path.
33223  */
jx9ExtractDirName(const char * zPath,int nByte,int * pLen)33224 JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen)
33225 {
33226 	const char *zEnd = &zPath[nByte - 1];
33227 	int c, d;
33228 	c = d = '/';
33229 #ifdef __WINNT__
33230 	d = '\\';
33231 #endif
33232 	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
33233 		zEnd--;
33234 	}
33235 	*pLen = (int)(zEnd-zPath);
33236 #ifdef __WINNT__
33237 	if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){
33238 		/* Normalize path on windows */
33239 		return "\\";
33240 	}
33241 #endif
33242 	if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){
33243 		/* No separator, return "." as the current directory */
33244 		*pLen = sizeof(char);
33245 		return ".";
33246 	}
33247 	if( (*pLen) == 0 ){
33248 		*pLen = sizeof(char);
33249 #ifdef __WINNT__
33250 		return "\\";
33251 #else
33252 		return "/";
33253 #endif
33254 	}
33255 	return zPath;
33256 }
33257 /*
33258  * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined.
33259  */
33260 #ifndef JX9_DISABLE_BUILTIN_FUNC
33261 /*
33262  * bool chdir(string $directory)
33263  *  Change the current directory.
33264  * Parameters
33265  *  $directory
33266  *   The new current directory
33267  * Return
33268  *  TRUE on success or FALSE on failure.
33269  */
jx9Vfs_chdir(jx9_context * pCtx,int nArg,jx9_value ** apArg)33270 static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33271 {
33272 	const char *zPath;
33273 	jx9_vfs *pVfs;
33274 	int rc;
33275 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33276 		/* Missing/Invalid argument, return FALSE */
33277 		jx9_result_bool(pCtx, 0);
33278 		return JX9_OK;
33279 	}
33280 	/* Point to the underlying vfs */
33281 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33282 	if( pVfs == 0 || pVfs->xChdir == 0 ){
33283 		/* IO routine not implemented, return NULL */
33284 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33285 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33286 			jx9_function_name(pCtx)
33287 			);
33288 		jx9_result_bool(pCtx, 0);
33289 		return JX9_OK;
33290 	}
33291 	/* Point to the desired directory */
33292 	zPath = jx9_value_to_string(apArg[0], 0);
33293 	/* Perform the requested operation */
33294 	rc = pVfs->xChdir(zPath);
33295 	/* IO return value */
33296 	jx9_result_bool(pCtx, rc == JX9_OK);
33297 	return JX9_OK;
33298 }
33299 /*
33300  * bool chroot(string $directory)
33301  *  Change the root directory.
33302  * Parameters
33303  *  $directory
33304  *   The path to change the root directory to
33305  * Return
33306  *  TRUE on success or FALSE on failure.
33307  */
jx9Vfs_chroot(jx9_context * pCtx,int nArg,jx9_value ** apArg)33308 static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
33309 {
33310 	const char *zPath;
33311 	jx9_vfs *pVfs;
33312 	int rc;
33313 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33314 		/* Missing/Invalid argument, return FALSE */
33315 		jx9_result_bool(pCtx, 0);
33316 		return JX9_OK;
33317 	}
33318 	/* Point to the underlying vfs */
33319 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33320 	if( pVfs == 0 || pVfs->xChroot == 0 ){
33321 		/* IO routine not implemented, return NULL */
33322 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33323 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33324 			jx9_function_name(pCtx)
33325 			);
33326 		jx9_result_bool(pCtx, 0);
33327 		return JX9_OK;
33328 	}
33329 	/* Point to the desired directory */
33330 	zPath = jx9_value_to_string(apArg[0], 0);
33331 	/* Perform the requested operation */
33332 	rc = pVfs->xChroot(zPath);
33333 	/* IO return value */
33334 	jx9_result_bool(pCtx, rc == JX9_OK);
33335 	return JX9_OK;
33336 }
33337 /*
33338  * string getcwd(void)
33339  *  Gets the current working directory.
33340  * Parameters
33341  *  None
33342  * Return
33343  *  Returns the current working directory on success, or FALSE on failure.
33344  */
jx9Vfs_getcwd(jx9_context * pCtx,int nArg,jx9_value ** apArg)33345 static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
33346 {
33347 	jx9_vfs *pVfs;
33348 	int rc;
33349 	/* Point to the underlying vfs */
33350 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33351 	if( pVfs == 0 || pVfs->xGetcwd == 0 ){
33352 		SXUNUSED(nArg); /* cc warning */
33353 		SXUNUSED(apArg);
33354 		/* IO routine not implemented, return NULL */
33355 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33356 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33357 			jx9_function_name(pCtx)
33358 			);
33359 		jx9_result_bool(pCtx, 0);
33360 		return JX9_OK;
33361 	}
33362 	jx9_result_string(pCtx, "", 0);
33363 	/* Perform the requested operation */
33364 	rc = pVfs->xGetcwd(pCtx);
33365 	if( rc != JX9_OK ){
33366 		/* Error, return FALSE */
33367 		jx9_result_bool(pCtx, 0);
33368 	}
33369 	return JX9_OK;
33370 }
33371 /*
33372  * bool rmdir(string $directory)
33373  *  Removes directory.
33374  * Parameters
33375  *  $directory
33376  *   The path to the directory
33377  * Return
33378  *  TRUE on success or FALSE on failure.
33379  */
jx9Vfs_rmdir(jx9_context * pCtx,int nArg,jx9_value ** apArg)33380 static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33381 {
33382 	const char *zPath;
33383 	jx9_vfs *pVfs;
33384 	int rc;
33385 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33386 		/* Missing/Invalid argument, return FALSE */
33387 		jx9_result_bool(pCtx, 0);
33388 		return JX9_OK;
33389 	}
33390 	/* Point to the underlying vfs */
33391 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33392 	if( pVfs == 0 || pVfs->xRmdir == 0 ){
33393 		/* IO routine not implemented, return NULL */
33394 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33395 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33396 			jx9_function_name(pCtx)
33397 			);
33398 		jx9_result_bool(pCtx, 0);
33399 		return JX9_OK;
33400 	}
33401 	/* Point to the desired directory */
33402 	zPath = jx9_value_to_string(apArg[0], 0);
33403 	/* Perform the requested operation */
33404 	rc = pVfs->xRmdir(zPath);
33405 	/* IO return value */
33406 	jx9_result_bool(pCtx, rc == JX9_OK);
33407 	return JX9_OK;
33408 }
33409 /*
33410  * bool is_dir(string $filename)
33411  *  Tells whether the given filename is a directory.
33412  * Parameters
33413  *  $filename
33414  *   Path to the file.
33415  * Return
33416  *  TRUE on success or FALSE on failure.
33417  */
jx9Vfs_is_dir(jx9_context * pCtx,int nArg,jx9_value ** apArg)33418 static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33419 {
33420 	const char *zPath;
33421 	jx9_vfs *pVfs;
33422 	int rc;
33423 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33424 		/* Missing/Invalid argument, return FALSE */
33425 		jx9_result_bool(pCtx, 0);
33426 		return JX9_OK;
33427 	}
33428 	/* Point to the underlying vfs */
33429 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33430 	if( pVfs == 0 || pVfs->xIsdir == 0 ){
33431 		/* IO routine not implemented, return NULL */
33432 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33433 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33434 			jx9_function_name(pCtx)
33435 			);
33436 		jx9_result_bool(pCtx, 0);
33437 		return JX9_OK;
33438 	}
33439 	/* Point to the desired directory */
33440 	zPath = jx9_value_to_string(apArg[0], 0);
33441 	/* Perform the requested operation */
33442 	rc = pVfs->xIsdir(zPath);
33443 	/* IO return value */
33444 	jx9_result_bool(pCtx, rc == JX9_OK);
33445 	return JX9_OK;
33446 }
33447 /*
33448  * bool mkdir(string $pathname[, int $mode = 0777])
33449  *  Make a directory.
33450  * Parameters
33451  *  $pathname
33452  *   The directory path.
33453  * $mode
33454  *  The mode is 0777 by default, which means the widest possible access.
33455  *  Note:
33456  *   mode is ignored on Windows.
33457  *   Note that you probably want to specify the mode as an octal number, which means
33458  *   it should have a leading zero. The mode is also modified by the current umask
33459  *   which you can change using umask().
33460  * Return
33461  *  TRUE on success or FALSE on failure.
33462  */
jx9Vfs_mkdir(jx9_context * pCtx,int nArg,jx9_value ** apArg)33463 static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
33464 {
33465 	int iRecursive = 0;
33466 	const char *zPath;
33467 	jx9_vfs *pVfs;
33468 	int iMode, rc;
33469 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33470 		/* Missing/Invalid argument, return FALSE */
33471 		jx9_result_bool(pCtx, 0);
33472 		return JX9_OK;
33473 	}
33474 	/* Point to the underlying vfs */
33475 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33476 	if( pVfs == 0 || pVfs->xMkdir == 0 ){
33477 		/* IO routine not implemented, return NULL */
33478 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33479 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33480 			jx9_function_name(pCtx)
33481 			);
33482 		jx9_result_bool(pCtx, 0);
33483 		return JX9_OK;
33484 	}
33485 	/* Point to the desired directory */
33486 	zPath = jx9_value_to_string(apArg[0], 0);
33487 #ifdef __WINNT__
33488 	iMode = 0;
33489 #else
33490 	/* Assume UNIX */
33491 	iMode = 0777;
33492 #endif
33493 	if( nArg > 1 ){
33494 		iMode = jx9_value_to_int(apArg[1]);
33495 		if( nArg > 2 ){
33496 			iRecursive = jx9_value_to_bool(apArg[2]);
33497 		}
33498 	}
33499 	/* Perform the requested operation */
33500 	rc = pVfs->xMkdir(zPath, iMode, iRecursive);
33501 	/* IO return value */
33502 	jx9_result_bool(pCtx, rc == JX9_OK);
33503 	return JX9_OK;
33504 }
33505 /*
33506  * bool rename(string $oldname, string $newname)
33507  *  Attempts to rename oldname to newname.
33508  * Parameters
33509  *  $oldname
33510  *   Old name.
33511  *  $newname
33512  *   New name.
33513  * Return
33514  *  TRUE on success or FALSE on failure.
33515  */
jx9Vfs_rename(jx9_context * pCtx,int nArg,jx9_value ** apArg)33516 static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
33517 {
33518 	const char *zOld, *zNew;
33519 	jx9_vfs *pVfs;
33520 	int rc;
33521 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
33522 		/* Missing/Invalid arguments, return FALSE */
33523 		jx9_result_bool(pCtx, 0);
33524 		return JX9_OK;
33525 	}
33526 	/* Point to the underlying vfs */
33527 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33528 	if( pVfs == 0 || pVfs->xRename == 0 ){
33529 		/* IO routine not implemented, return NULL */
33530 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33531 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33532 			jx9_function_name(pCtx)
33533 			);
33534 		jx9_result_bool(pCtx, 0);
33535 		return JX9_OK;
33536 	}
33537 	/* Perform the requested operation */
33538 	zOld = jx9_value_to_string(apArg[0], 0);
33539 	zNew = jx9_value_to_string(apArg[1], 0);
33540 	rc = pVfs->xRename(zOld, zNew);
33541 	/* IO result */
33542 	jx9_result_bool(pCtx, rc == JX9_OK );
33543 	return JX9_OK;
33544 }
33545 /*
33546  * string realpath(string $path)
33547  *  Returns canonicalized absolute pathname.
33548  * Parameters
33549  *  $path
33550  *   Target path.
33551  * Return
33552  *  Canonicalized absolute pathname on success. or FALSE on failure.
33553  */
jx9Vfs_realpath(jx9_context * pCtx,int nArg,jx9_value ** apArg)33554 static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
33555 {
33556 	const char *zPath;
33557 	jx9_vfs *pVfs;
33558         int rc;
33559 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33560 		/* Missing/Invalid argument, return FALSE */
33561 		jx9_result_bool(pCtx, 0);
33562 		return JX9_OK;
33563 	}
33564 	/* Point to the underlying vfs */
33565 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33566 	if( pVfs == 0 || pVfs->xRealpath == 0 ){
33567 		/* IO routine not implemented, return NULL */
33568 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33569 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33570 			jx9_function_name(pCtx)
33571 			);
33572 		jx9_result_bool(pCtx, 0);
33573 		return JX9_OK;
33574 	}
33575 	/* Set an empty string untnil the underlying OS interface change that */
33576 	jx9_result_string(pCtx, "", 0);
33577 	/* Perform the requested operation */
33578 	zPath = jx9_value_to_string(apArg[0], 0);
33579 	rc = pVfs->xRealpath(zPath, pCtx);
33580 	if( rc != JX9_OK ){
33581 	 jx9_result_bool(pCtx, 0);
33582 	}
33583 	return JX9_OK;
33584 }
33585 /*
33586  * int sleep(int $seconds)
33587  *  Delays the program execution for the given number of seconds.
33588  * Parameters
33589  *  $seconds
33590  *   Halt time in seconds.
33591  * Return
33592  *  Zero on success or FALSE on failure.
33593  */
jx9Vfs_sleep(jx9_context * pCtx,int nArg,jx9_value ** apArg)33594 static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
33595 {
33596 	jx9_vfs *pVfs;
33597 	int rc, nSleep;
33598 	if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
33599 		/* Missing/Invalid argument, return FALSE */
33600 		jx9_result_bool(pCtx, 0);
33601 		return JX9_OK;
33602 	}
33603 	/* Point to the underlying vfs */
33604 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33605 	if( pVfs == 0 || pVfs->xSleep == 0 ){
33606 		/* IO routine not implemented, return NULL */
33607 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33608 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33609 			jx9_function_name(pCtx)
33610 			);
33611 		jx9_result_bool(pCtx, 0);
33612 		return JX9_OK;
33613 	}
33614 	/* Amount to sleep */
33615 	nSleep = jx9_value_to_int(apArg[0]);
33616 	if( nSleep < 0 ){
33617 		/* Invalid value, return FALSE */
33618 		jx9_result_bool(pCtx, 0);
33619 		return JX9_OK;
33620 	}
33621 	/* Perform the requested operation (Microseconds) */
33622 	rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
33623 	if( rc != JX9_OK ){
33624 		/* Return FALSE */
33625 		jx9_result_bool(pCtx, 0);
33626 	}else{
33627 		/* Return zero */
33628 		jx9_result_int(pCtx, 0);
33629 	}
33630 	return JX9_OK;
33631 }
33632 /*
33633  * void usleep(int $micro_seconds)
33634  *  Delays program execution for the given number of micro seconds.
33635  * Parameters
33636  *  $micro_seconds
33637  *   Halt time in micro seconds. A micro second is one millionth of a second.
33638  * Return
33639  *  None.
33640  */
jx9Vfs_usleep(jx9_context * pCtx,int nArg,jx9_value ** apArg)33641 static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
33642 {
33643 	jx9_vfs *pVfs;
33644 	int nSleep;
33645 	if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
33646 		/* Missing/Invalid argument, return immediately */
33647 		return JX9_OK;
33648 	}
33649 	/* Point to the underlying vfs */
33650 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33651 	if( pVfs == 0 || pVfs->xSleep == 0 ){
33652 		/* IO routine not implemented, return NULL */
33653 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33654 			"IO routine(%s) not implemented in the underlying VFS",
33655 			jx9_function_name(pCtx)
33656 			);
33657 		return JX9_OK;
33658 	}
33659 	/* Amount to sleep */
33660 	nSleep = jx9_value_to_int(apArg[0]);
33661 	if( nSleep < 0 ){
33662 		/* Invalid value, return immediately */
33663 		return JX9_OK;
33664 	}
33665 	/* Perform the requested operation (Microseconds) */
33666 	pVfs->xSleep((unsigned int)nSleep);
33667 	return JX9_OK;
33668 }
33669 /*
33670  * bool unlink (string $filename)
33671  *  Delete a file.
33672  * Parameters
33673  *  $filename
33674  *   Path to the file.
33675  * Return
33676  *  TRUE on success or FALSE on failure.
33677  */
jx9Vfs_unlink(jx9_context * pCtx,int nArg,jx9_value ** apArg)33678 static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
33679 {
33680 	const char *zPath;
33681 	jx9_vfs *pVfs;
33682 	int rc;
33683 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33684 		/* Missing/Invalid argument, return FALSE */
33685 		jx9_result_bool(pCtx, 0);
33686 		return JX9_OK;
33687 	}
33688 	/* Point to the underlying vfs */
33689 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33690 	if( pVfs == 0 || pVfs->xUnlink == 0 ){
33691 		/* IO routine not implemented, return NULL */
33692 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33693 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33694 			jx9_function_name(pCtx)
33695 			);
33696 		jx9_result_bool(pCtx, 0);
33697 		return JX9_OK;
33698 	}
33699 	/* Point to the desired directory */
33700 	zPath = jx9_value_to_string(apArg[0], 0);
33701 	/* Perform the requested operation */
33702 	rc = pVfs->xUnlink(zPath);
33703 	/* IO return value */
33704 	jx9_result_bool(pCtx, rc == JX9_OK);
33705 	return JX9_OK;
33706 }
33707 /*
33708  * bool chmod(string $filename, int $mode)
33709  *  Attempts to change the mode of the specified file to that given in mode.
33710  * Parameters
33711  *  $filename
33712  *   Path to the file.
33713  * $mode
33714  *   Mode (Must be an integer)
33715  * Return
33716  *  TRUE on success or FALSE on failure.
33717  */
jx9Vfs_chmod(jx9_context * pCtx,int nArg,jx9_value ** apArg)33718 static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
33719 {
33720 	const char *zPath;
33721 	jx9_vfs *pVfs;
33722 	int iMode;
33723 	int rc;
33724 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33725 		/* Missing/Invalid argument, return FALSE */
33726 		jx9_result_bool(pCtx, 0);
33727 		return JX9_OK;
33728 	}
33729 	/* Point to the underlying vfs */
33730 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33731 	if( pVfs == 0 || pVfs->xChmod == 0 ){
33732 		/* IO routine not implemented, return NULL */
33733 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33734 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33735 			jx9_function_name(pCtx)
33736 			);
33737 		jx9_result_bool(pCtx, 0);
33738 		return JX9_OK;
33739 	}
33740 	/* Point to the desired directory */
33741 	zPath = jx9_value_to_string(apArg[0], 0);
33742 	/* Extract the mode */
33743 	iMode = jx9_value_to_int(apArg[1]);
33744 	/* Perform the requested operation */
33745 	rc = pVfs->xChmod(zPath, iMode);
33746 	/* IO return value */
33747 	jx9_result_bool(pCtx, rc == JX9_OK);
33748 	return JX9_OK;
33749 }
33750 /*
33751  * bool chown(string $filename, string $user)
33752  *  Attempts to change the owner of the file filename to user user.
33753  * Parameters
33754  *  $filename
33755  *   Path to the file.
33756  * $user
33757  *   Username.
33758  * Return
33759  *  TRUE on success or FALSE on failure.
33760  */
jx9Vfs_chown(jx9_context * pCtx,int nArg,jx9_value ** apArg)33761 static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
33762 {
33763 	const char *zPath, *zUser;
33764 	jx9_vfs *pVfs;
33765 	int rc;
33766 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33767 		/* Missing/Invalid arguments, return FALSE */
33768 		jx9_result_bool(pCtx, 0);
33769 		return JX9_OK;
33770 	}
33771 	/* Point to the underlying vfs */
33772 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33773 	if( pVfs == 0 || pVfs->xChown == 0 ){
33774 		/* IO routine not implemented, return NULL */
33775 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33776 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33777 			jx9_function_name(pCtx)
33778 			);
33779 		jx9_result_bool(pCtx, 0);
33780 		return JX9_OK;
33781 	}
33782 	/* Point to the desired directory */
33783 	zPath = jx9_value_to_string(apArg[0], 0);
33784 	/* Extract the user */
33785 	zUser = jx9_value_to_string(apArg[1], 0);
33786 	/* Perform the requested operation */
33787 	rc = pVfs->xChown(zPath, zUser);
33788 	/* IO return value */
33789 	jx9_result_bool(pCtx, rc == JX9_OK);
33790 	return JX9_OK;
33791 }
33792 /*
33793  * bool chgrp(string $filename, string $group)
33794  *  Attempts to change the group of the file filename to group.
33795  * Parameters
33796  *  $filename
33797  *   Path to the file.
33798  * $group
33799  *   groupname.
33800  * Return
33801  *  TRUE on success or FALSE on failure.
33802  */
jx9Vfs_chgrp(jx9_context * pCtx,int nArg,jx9_value ** apArg)33803 static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
33804 {
33805 	const char *zPath, *zGroup;
33806 	jx9_vfs *pVfs;
33807 	int rc;
33808 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
33809 		/* Missing/Invalid arguments, return FALSE */
33810 		jx9_result_bool(pCtx, 0);
33811 		return JX9_OK;
33812 	}
33813 	/* Point to the underlying vfs */
33814 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33815 	if( pVfs == 0 || pVfs->xChgrp == 0 ){
33816 		/* IO routine not implemented, return NULL */
33817 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33818 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33819 			jx9_function_name(pCtx)
33820 			);
33821 		jx9_result_bool(pCtx, 0);
33822 		return JX9_OK;
33823 	}
33824 	/* Point to the desired directory */
33825 	zPath = jx9_value_to_string(apArg[0], 0);
33826 	/* Extract the user */
33827 	zGroup = jx9_value_to_string(apArg[1], 0);
33828 	/* Perform the requested operation */
33829 	rc = pVfs->xChgrp(zPath, zGroup);
33830 	/* IO return value */
33831 	jx9_result_bool(pCtx, rc == JX9_OK);
33832 	return JX9_OK;
33833 }
33834 /*
33835  * int64 disk_free_space(string $directory)
33836  *  Returns available space on filesystem or disk partition.
33837  * Parameters
33838  *  $directory
33839  *   A directory of the filesystem or disk partition.
33840  * Return
33841  *  Returns the number of available bytes as a 64-bit integer or FALSE on failure.
33842  */
jx9Vfs_disk_free_space(jx9_context * pCtx,int nArg,jx9_value ** apArg)33843 static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
33844 {
33845 	const char *zPath;
33846 	jx9_int64 iSize;
33847 	jx9_vfs *pVfs;
33848 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33849 		/* Missing/Invalid argument, return FALSE */
33850 		jx9_result_bool(pCtx, 0);
33851 		return JX9_OK;
33852 	}
33853 	/* Point to the underlying vfs */
33854 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33855 	if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
33856 		/* IO routine not implemented, return NULL */
33857 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33858 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33859 			jx9_function_name(pCtx)
33860 			);
33861 		jx9_result_bool(pCtx, 0);
33862 		return JX9_OK;
33863 	}
33864 	/* Point to the desired directory */
33865 	zPath = jx9_value_to_string(apArg[0], 0);
33866 	/* Perform the requested operation */
33867 	iSize = pVfs->xFreeSpace(zPath);
33868 	/* IO return value */
33869 	jx9_result_int64(pCtx, iSize);
33870 	return JX9_OK;
33871 }
33872 /*
33873  * int64 disk_total_space(string $directory)
33874  *  Returns the total size of a filesystem or disk partition.
33875  * Parameters
33876  *  $directory
33877  *   A directory of the filesystem or disk partition.
33878  * Return
33879  *  Returns the number of available bytes as a 64-bit integer or FALSE on failure.
33880  */
jx9Vfs_disk_total_space(jx9_context * pCtx,int nArg,jx9_value ** apArg)33881 static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
33882 {
33883 	const char *zPath;
33884 	jx9_int64 iSize;
33885 	jx9_vfs *pVfs;
33886 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
33887 		/* Missing/Invalid argument, return FALSE */
33888 		jx9_result_bool(pCtx, 0);
33889 		return JX9_OK;
33890 	}
33891 	/* Point to the underlying vfs */
33892 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
33893 	if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
33894 		/* IO routine not implemented, return NULL */
33895 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
33896 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
33897 			jx9_function_name(pCtx)
33898 			);
33899 		jx9_result_bool(pCtx, 0);
33900 		return JX9_OK;
33901 	}
33902 	/* Point to the desired directory */
33903 	zPath = jx9_value_to_string(apArg[0], 0);
33904 	/* Perform the requested operation */
33905 	iSize = pVfs->xTotalSpace(zPath);
33906 	/* IO return value */
33907 	jx9_result_int64(pCtx, iSize);
33908 	return JX9_OK;
33909 }
33910 /*
33911  * bool file_exists(string $filename)
33912  *  Checks whether a file or directory exists.
33913  * Parameters
33914  *  $filename
33915  *   Path to the file.
33916  * Return
33917  *  TRUE on success or FALSE on failure.
33918  */
jx9Vfs_file_exists(jx9_context * pCtx,int nArg,jx9_value ** apArg)33919 static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
33920 {
33921 	const char *zPath;
33922 	jx9_vfs *pVfs;
33923 	int rc;
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->xFileExists == 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 	rc = pVfs->xFileExists(zPath);
33944 	/* IO return value */
33945 	jx9_result_bool(pCtx, rc == JX9_OK);
33946 	return JX9_OK;
33947 }
33948 /*
33949  * int64 file_size(string $filename)
33950  *  Gets the size for the given file.
33951  * Parameters
33952  *  $filename
33953  *   Path to the file.
33954  * Return
33955  *  File size on success or FALSE on failure.
33956  */
jx9Vfs_file_size(jx9_context * pCtx,int nArg,jx9_value ** apArg)33957 static int jx9Vfs_file_size(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->xFileSize == 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->xFileSize(zPath);
33982 	/* IO return value */
33983 	jx9_result_int64(pCtx, iSize);
33984 	return JX9_OK;
33985 }
33986 /*
33987  * int64 fileatime(string $filename)
33988  *  Gets the last access time of the given file.
33989  * Parameters
33990  *  $filename
33991  *   Path to the file.
33992  * Return
33993  *  File atime on success or FALSE on failure.
33994  */
jx9Vfs_file_atime(jx9_context * pCtx,int nArg,jx9_value ** apArg)33995 static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
33996 {
33997 	const char *zPath;
33998 	jx9_int64 iTime;
33999 	jx9_vfs *pVfs;
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->xFileAtime == 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 	iTime = pVfs->xFileAtime(zPath);
34020 	/* IO return value */
34021 	jx9_result_int64(pCtx, iTime);
34022 	return JX9_OK;
34023 }
34024 /*
34025  * int64 filemtime(string $filename)
34026  *  Gets file modification time.
34027  * Parameters
34028  *  $filename
34029  *   Path to the file.
34030  * Return
34031  *  File mtime on success or FALSE on failure.
34032  */
jx9Vfs_file_mtime(jx9_context * pCtx,int nArg,jx9_value ** apArg)34033 static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
34034 {
34035 	const char *zPath;
34036 	jx9_int64 iTime;
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->xFileMtime == 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 	iTime = pVfs->xFileMtime(zPath);
34058 	/* IO return value */
34059 	jx9_result_int64(pCtx, iTime);
34060 	return JX9_OK;
34061 }
34062 /*
34063  * int64 filectime(string $filename)
34064  *  Gets inode change time of file.
34065  * Parameters
34066  *  $filename
34067  *   Path to the file.
34068  * Return
34069  *  File ctime on success or FALSE on failure.
34070  */
jx9Vfs_file_ctime(jx9_context * pCtx,int nArg,jx9_value ** apArg)34071 static int jx9Vfs_file_ctime(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->xFileCtime == 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->xFileCtime(zPath);
34096 	/* IO return value */
34097 	jx9_result_int64(pCtx, iTime);
34098 	return JX9_OK;
34099 }
34100 /*
34101  * bool is_file(string $filename)
34102  *  Tells whether the filename is a regular file.
34103  * Parameters
34104  *  $filename
34105  *   Path to the file.
34106  * Return
34107  *  TRUE on success or FALSE on failure.
34108  */
jx9Vfs_is_file(jx9_context * pCtx,int nArg,jx9_value ** apArg)34109 static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
34110 {
34111 	const char *zPath;
34112 	jx9_vfs *pVfs;
34113 	int rc;
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->xIsfile == 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 	rc = pVfs->xIsfile(zPath);
34134 	/* IO return value */
34135 	jx9_result_bool(pCtx, rc == JX9_OK);
34136 	return JX9_OK;
34137 }
34138 /*
34139  * bool is_link(string $filename)
34140  *  Tells whether the filename is a symbolic link.
34141  * Parameters
34142  *  $filename
34143  *   Path to the file.
34144  * Return
34145  *  TRUE on success or FALSE on failure.
34146  */
jx9Vfs_is_link(jx9_context * pCtx,int nArg,jx9_value ** apArg)34147 static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
34148 {
34149 	const char *zPath;
34150 	jx9_vfs *pVfs;
34151 	int rc;
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->xIslink == 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 	rc = pVfs->xIslink(zPath);
34172 	/* IO return value */
34173 	jx9_result_bool(pCtx, rc == JX9_OK);
34174 	return JX9_OK;
34175 }
34176 /*
34177  * bool is_readable(string $filename)
34178  *  Tells whether a file exists and is readable.
34179  * Parameters
34180  *  $filename
34181  *   Path to the file.
34182  * Return
34183  *  TRUE on success or FALSE on failure.
34184  */
jx9Vfs_is_readable(jx9_context * pCtx,int nArg,jx9_value ** apArg)34185 static int jx9Vfs_is_readable(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->xReadable == 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->xReadable(zPath);
34210 	/* IO return value */
34211 	jx9_result_bool(pCtx, rc == JX9_OK);
34212 	return JX9_OK;
34213 }
34214 /*
34215  * bool is_writable(string $filename)
34216  *  Tells whether the filename is writable.
34217  * Parameters
34218  *  $filename
34219  *   Path to the file.
34220  * Return
34221  *  TRUE on success or FALSE on failure.
34222  */
jx9Vfs_is_writable(jx9_context * pCtx,int nArg,jx9_value ** apArg)34223 static int jx9Vfs_is_writable(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->xWritable == 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->xWritable(zPath);
34248 	/* IO return value */
34249 	jx9_result_bool(pCtx, rc == JX9_OK);
34250 	return JX9_OK;
34251 }
34252 /*
34253  * bool is_executable(string $filename)
34254  *  Tells whether the filename is executable.
34255  * Parameters
34256  *  $filename
34257  *   Path to the file.
34258  * Return
34259  *  TRUE on success or FALSE on failure.
34260  */
jx9Vfs_is_executable(jx9_context * pCtx,int nArg,jx9_value ** apArg)34261 static int jx9Vfs_is_executable(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->xExecutable == 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->xExecutable(zPath);
34286 	/* IO return value */
34287 	jx9_result_bool(pCtx, rc == JX9_OK);
34288 	return JX9_OK;
34289 }
34290 /*
34291  * string filetype(string $filename)
34292  *  Gets file type.
34293  * Parameters
34294  *  $filename
34295  *   Path to the file.
34296  * Return
34297  *  The type of the file. Possible values are fifo, char, dir, block, link
34298  *  file, socket and unknown.
34299  */
jx9Vfs_filetype(jx9_context * pCtx,int nArg,jx9_value ** apArg)34300 static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
34301 {
34302 	const char *zPath;
34303 	jx9_vfs *pVfs;
34304 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34305 		/* Missing/Invalid argument, return 'unknown' */
34306 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
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->xFiletype == 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 	/* Set the empty string as the default return value */
34323 	jx9_result_string(pCtx, "", 0);
34324 	/* Perform the requested operation */
34325 	pVfs->xFiletype(zPath, pCtx);
34326 	return JX9_OK;
34327 }
34328 /*
34329  * array stat(string $filename)
34330  *  Gives information about a file.
34331  * Parameters
34332  *  $filename
34333  *   Path to the file.
34334  * Return
34335  *  An associative array on success holding the following entries on success
34336  *  0   dev     device number
34337  * 1    ino     inode number (zero on windows)
34338  * 2    mode    inode protection mode
34339  * 3    nlink   number of links
34340  * 4    uid     userid of owner (zero on windows)
34341  * 5    gid     groupid of owner (zero on windows)
34342  * 6    rdev    device type, if inode device
34343  * 7    size    size in bytes
34344  * 8    atime   time of last access (Unix timestamp)
34345  * 9    mtime   time of last modification (Unix timestamp)
34346  * 10   ctime   time of last inode change (Unix timestamp)
34347  * 11   blksize blocksize of filesystem IO (zero on windows)
34348  * 12   blocks  number of 512-byte blocks allocated.
34349  * Note:
34350  *  FALSE is returned on failure.
34351  */
jx9Vfs_stat(jx9_context * pCtx,int nArg,jx9_value ** apArg)34352 static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
34353 {
34354 	jx9_value *pArray, *pValue;
34355 	const char *zPath;
34356 	jx9_vfs *pVfs;
34357 	int rc;
34358 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34359 		/* Missing/Invalid argument, return FALSE */
34360 		jx9_result_bool(pCtx, 0);
34361 		return JX9_OK;
34362 	}
34363 	/* Point to the underlying vfs */
34364 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34365 	if( pVfs == 0 || pVfs->xStat == 0 ){
34366 		/* IO routine not implemented, return NULL */
34367 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34368 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34369 			jx9_function_name(pCtx)
34370 			);
34371 		jx9_result_bool(pCtx, 0);
34372 		return JX9_OK;
34373 	}
34374 	/* Create the array and the working value */
34375 	pArray = jx9_context_new_array(pCtx);
34376 	pValue = jx9_context_new_scalar(pCtx);
34377 	if( pArray == 0 || pValue == 0 ){
34378 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
34379 		jx9_result_bool(pCtx, 0);
34380 		return JX9_OK;
34381 	}
34382 	/* Extract the file path */
34383 	zPath = jx9_value_to_string(apArg[0], 0);
34384 	/* Perform the requested operation */
34385 	rc = pVfs->xStat(zPath, pArray, pValue);
34386 	if( rc != JX9_OK ){
34387 		/* IO error, return FALSE */
34388 		jx9_result_bool(pCtx, 0);
34389 	}else{
34390 		/* Return the associative array */
34391 		jx9_result_value(pCtx, pArray);
34392 	}
34393 	/* Don't worry about freeing memory here, everything will be released
34394 	 * automatically as soon we return from this function. */
34395 	return JX9_OK;
34396 }
34397 /*
34398  * array lstat(string $filename)
34399  *  Gives information about a file or symbolic link.
34400  * Parameters
34401  *  $filename
34402  *   Path to the file.
34403  * Return
34404  *  An associative array on success holding the following entries on success
34405  *  0   dev     device number
34406  * 1    ino     inode number (zero on windows)
34407  * 2    mode    inode protection mode
34408  * 3    nlink   number of links
34409  * 4    uid     userid of owner (zero on windows)
34410  * 5    gid     groupid of owner (zero on windows)
34411  * 6    rdev    device type, if inode device
34412  * 7    size    size in bytes
34413  * 8    atime   time of last access (Unix timestamp)
34414  * 9    mtime   time of last modification (Unix timestamp)
34415  * 10   ctime   time of last inode change (Unix timestamp)
34416  * 11   blksize blocksize of filesystem IO (zero on windows)
34417  * 12   blocks  number of 512-byte blocks allocated.
34418  * Note:
34419  *  FALSE is returned on failure.
34420  */
jx9Vfs_lstat(jx9_context * pCtx,int nArg,jx9_value ** apArg)34421 static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
34422 {
34423 	jx9_value *pArray, *pValue;
34424 	const char *zPath;
34425 	jx9_vfs *pVfs;
34426 	int rc;
34427 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34428 		/* Missing/Invalid argument, return FALSE */
34429 		jx9_result_bool(pCtx, 0);
34430 		return JX9_OK;
34431 	}
34432 	/* Point to the underlying vfs */
34433 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34434 	if( pVfs == 0 || pVfs->xlStat == 0 ){
34435 		/* IO routine not implemented, return NULL */
34436 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34437 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34438 			jx9_function_name(pCtx)
34439 			);
34440 		jx9_result_bool(pCtx, 0);
34441 		return JX9_OK;
34442 	}
34443 	/* Create the array and the working value */
34444 	pArray = jx9_context_new_array(pCtx);
34445 	pValue = jx9_context_new_scalar(pCtx);
34446 	if( pArray == 0 || pValue == 0 ){
34447 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
34448 		jx9_result_bool(pCtx, 0);
34449 		return JX9_OK;
34450 	}
34451 	/* Extract the file path */
34452 	zPath = jx9_value_to_string(apArg[0], 0);
34453 	/* Perform the requested operation */
34454 	rc = pVfs->xlStat(zPath, pArray, pValue);
34455 	if( rc != JX9_OK ){
34456 		/* IO error, return FALSE */
34457 		jx9_result_bool(pCtx, 0);
34458 	}else{
34459 		/* Return the associative array */
34460 		jx9_result_value(pCtx, pArray);
34461 	}
34462 	/* Don't worry about freeing memory here, everything will be released
34463 	 * automatically as soon we return from this function. */
34464 	return JX9_OK;
34465 }
34466 /*
34467  * string getenv(string $varname)
34468  *  Gets the value of an environment variable.
34469  * Parameters
34470  *  $varname
34471  *   The variable name.
34472  * Return
34473  *  Returns the value of the environment variable varname, or FALSE if the environment
34474  * variable varname does not exist.
34475  */
jx9Vfs_getenv(jx9_context * pCtx,int nArg,jx9_value ** apArg)34476 static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
34477 {
34478 	const char *zEnv;
34479 	jx9_vfs *pVfs;
34480 	int iLen;
34481 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34482 		/* Missing/Invalid argument, return FALSE */
34483 		jx9_result_bool(pCtx, 0);
34484 		return JX9_OK;
34485 	}
34486 	/* Point to the underlying vfs */
34487 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34488 	if( pVfs == 0 || pVfs->xGetenv == 0 ){
34489 		/* IO routine not implemented, return NULL */
34490 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34491 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34492 			jx9_function_name(pCtx)
34493 			);
34494 		jx9_result_bool(pCtx, 0);
34495 		return JX9_OK;
34496 	}
34497 	/* Extract the environment variable */
34498 	zEnv = jx9_value_to_string(apArg[0], &iLen);
34499 	/* Set a boolean FALSE as the default return value */
34500 	jx9_result_bool(pCtx, 0);
34501 	if( iLen < 1 ){
34502 		/* Empty string */
34503 		return JX9_OK;
34504 	}
34505 	/* Perform the requested operation */
34506 	pVfs->xGetenv(zEnv, pCtx);
34507 	return JX9_OK;
34508 }
34509 /*
34510  * bool putenv(string $settings)
34511  *  Set the value of an environment variable.
34512  * Parameters
34513  *  $setting
34514  *   The setting, like "FOO=BAR"
34515  * Return
34516  *  TRUE on success or FALSE on failure.
34517  */
jx9Vfs_putenv(jx9_context * pCtx,int nArg,jx9_value ** apArg)34518 static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
34519 {
34520 	const char *zName, *zValue;
34521 	char *zSettings, *zEnd;
34522 	jx9_vfs *pVfs;
34523 	int iLen, rc;
34524 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34525 		/* Missing/Invalid argument, return FALSE */
34526 		jx9_result_bool(pCtx, 0);
34527 		return JX9_OK;
34528 	}
34529 	/* Extract the setting variable */
34530 	zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
34531 	if( iLen < 1 ){
34532 		/* Empty string, return FALSE */
34533 		jx9_result_bool(pCtx, 0);
34534 		return JX9_OK;
34535 	}
34536 	/* Parse the setting */
34537 	zEnd = &zSettings[iLen];
34538 	zValue = 0;
34539 	zName = zSettings;
34540 	while( zSettings < zEnd ){
34541 		if( zSettings[0] == '=' ){
34542 			/* Null terminate the name */
34543 			zSettings[0] = 0;
34544 			zValue = &zSettings[1];
34545 			break;
34546 		}
34547 		zSettings++;
34548 	}
34549 	/* Install the environment variable in the $_Env array */
34550 	if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){
34551 		/* Invalid settings, retun FALSE */
34552 		jx9_result_bool(pCtx, 0);
34553 		if( zSettings  < zEnd ){
34554 			zSettings[0] = '=';
34555 		}
34556 		return JX9_OK;
34557 	}
34558 	jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
34559 	/* Point to the underlying vfs */
34560 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34561 	if( pVfs == 0 || pVfs->xSetenv == 0 ){
34562 		/* IO routine not implemented, return NULL */
34563 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34564 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34565 			jx9_function_name(pCtx)
34566 			);
34567 		jx9_result_bool(pCtx, 0);
34568 		zSettings[0] = '=';
34569 		return JX9_OK;
34570 	}
34571 	/* Perform the requested operation */
34572 	rc = pVfs->xSetenv(zName, zValue);
34573 	jx9_result_bool(pCtx, rc == JX9_OK );
34574 	zSettings[0] = '=';
34575 	return JX9_OK;
34576 }
34577 /*
34578  * bool touch(string $filename[, int64 $time = time()[, int64 $atime]])
34579  *  Sets access and modification time of file.
34580  * Note: On windows
34581  *   If the file does not exists, it will not be created.
34582  * Parameters
34583  *  $filename
34584  *   The name of the file being touched.
34585  *  $time
34586  *   The touch time. If time is not supplied, the current system time is used.
34587  * $atime
34588  *   If present, the access time of the given filename is set to the value of atime.
34589  *   Otherwise, it is set to the value passed to the time parameter. If neither are
34590  *   present, the current system time is used.
34591  * Return
34592  *  TRUE on success or FALSE on failure.
34593 */
jx9Vfs_touch(jx9_context * pCtx,int nArg,jx9_value ** apArg)34594 static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
34595 {
34596 	jx9_int64 nTime, nAccess;
34597 	const char *zFile;
34598 	jx9_vfs *pVfs;
34599 	int 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 	/* Point to the underlying vfs */
34606 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
34607 	if( pVfs == 0 || pVfs->xTouch == 0 ){
34608 		/* IO routine not implemented, return NULL */
34609 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
34610 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
34611 			jx9_function_name(pCtx)
34612 			);
34613 		jx9_result_bool(pCtx, 0);
34614 		return JX9_OK;
34615 	}
34616 	/* Perform the requested operation */
34617 	nTime = nAccess = -1;
34618 	zFile = jx9_value_to_string(apArg[0], 0);
34619 	if( nArg > 1 ){
34620 		nTime = jx9_value_to_int64(apArg[1]);
34621 		if( nArg > 2 ){
34622 			nAccess = jx9_value_to_int64(apArg[1]);
34623 		}else{
34624 			nAccess = nTime;
34625 		}
34626 	}
34627 	rc = pVfs->xTouch(zFile, nTime, nAccess);
34628 	/* IO result */
34629 	jx9_result_bool(pCtx, rc == JX9_OK);
34630 	return JX9_OK;
34631 }
34632 /*
34633  * Path processing functions that do not need access to the VFS layer
34634  * Authors:
34635  *    Symisc Systems, devel@symisc.net.
34636  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
34637  * Status:
34638  *    Stable.
34639  */
34640 /*
34641  * string dirname(string $path)
34642  *  Returns parent directory's path.
34643  * Parameters
34644  * $path
34645  *  Target path.
34646  *  On Windows, both slash (/) and backslash (\) are used as directory separator character.
34647  *  In other environments, it is the forward slash (/).
34648  * Return
34649  *  The path of the parent directory. If there are no slashes in path, a dot ('.')
34650  *  is returned, indicating the current directory.
34651  */
jx9Builtin_dirname(jx9_context * pCtx,int nArg,jx9_value ** apArg)34652 static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
34653 {
34654 	const char *zPath, *zDir;
34655 	int iLen, iDirlen;
34656 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34657 		/* Missing/Invalid arguments, return the empty string */
34658 		jx9_result_string(pCtx, "", 0);
34659 		return JX9_OK;
34660 	}
34661 	/* Point to the target path */
34662 	zPath = jx9_value_to_string(apArg[0], &iLen);
34663 	if( iLen < 1 ){
34664 		/* Reuturn "." */
34665 		jx9_result_string(pCtx, ".", sizeof(char));
34666 		return JX9_OK;
34667 	}
34668 	/* Perform the requested operation */
34669 	zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
34670 	/* Return directory name */
34671 	jx9_result_string(pCtx, zDir, iDirlen);
34672 	return JX9_OK;
34673 }
34674 /*
34675  * string basename(string $path[, string $suffix ])
34676  *  Returns trailing name component of path.
34677  * Parameters
34678  * $path
34679  *  Target path.
34680  *  On Windows, both slash (/) and backslash (\) are used as directory separator character.
34681  *  In other environments, it is the forward slash (/).
34682  * $suffix
34683  *  If the name component ends in suffix this will also be cut off.
34684  * Return
34685  *  The base name of the given path.
34686  */
jx9Builtin_basename(jx9_context * pCtx,int nArg,jx9_value ** apArg)34687 static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
34688 {
34689 	const char *zPath, *zBase, *zEnd;
34690 	int c, d, iLen;
34691 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34692 		/* Missing/Invalid argument, return the empty string */
34693 		jx9_result_string(pCtx, "", 0);
34694 		return JX9_OK;
34695 	}
34696 	c = d = '/';
34697 #ifdef __WINNT__
34698 	d = '\\';
34699 #endif
34700 	/* Point to the target path */
34701 	zPath = jx9_value_to_string(apArg[0], &iLen);
34702 	if( iLen < 1 ){
34703 		/* Empty string */
34704 		jx9_result_string(pCtx, "", 0);
34705 		return JX9_OK;
34706 	}
34707 	/* Perform the requested operation */
34708 	zEnd = &zPath[iLen - 1];
34709 	/* Ignore trailing '/' */
34710 	while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){
34711 		zEnd--;
34712 	}
34713 	iLen = (int)(&zEnd[1]-zPath);
34714 	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
34715 		zEnd--;
34716 	}
34717 	zBase = (zEnd > zPath) ? &zEnd[1] : zPath;
34718 	zEnd = &zPath[iLen];
34719 	if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
34720 		const char *zSuffix;
34721 		int nSuffix;
34722 		/* Strip suffix */
34723 		zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
34724 		if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
34725 			zEnd -= nSuffix;
34726 		}
34727 	}
34728 	/* Store the basename */
34729 	jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
34730 	return JX9_OK;
34731 }
34732 /*
34733  * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
34734  *  Returns information about a file path.
34735  * Parameter
34736  *  $path
34737  *   The path to be parsed.
34738  *  $options
34739  *    If present, specifies a specific element to be returned; one of
34740  *      PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME.
34741  * Return
34742  *  If the options parameter is not passed, an associative array containing the following
34743  *  elements is returned: dirname, basename, extension (if any), and filename.
34744  *  If options is present, returns a string containing the requested element.
34745  */
34746 typedef struct path_info path_info;
34747 struct path_info
34748 {
34749 	SyString sDir; /* Directory [i.e: /var/www] */
34750 	SyString sBasename; /* Basename [i.e httpd.conf] */
34751 	SyString sExtension; /* File extension [i.e xml, pdf..] */
34752 	SyString sFilename;  /* Filename */
34753 };
34754 /*
34755  * Extract path fields.
34756  */
ExtractPathInfo(const char * zPath,int nByte,path_info * pOut)34757 static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut)
34758 {
34759 	const char *zPtr, *zEnd = &zPath[nByte - 1];
34760 	SyString *pCur;
34761 	int c, d;
34762 	c = d = '/';
34763 #ifdef __WINNT__
34764 	d = '\\';
34765 #endif
34766 	/* Zero the structure */
34767 	SyZero(pOut, sizeof(path_info));
34768 	/* Handle special case */
34769 	if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){
34770 #ifdef __WINNT__
34771 		SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
34772 #else
34773 		SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
34774 #endif
34775 		return SXRET_OK;
34776 	}
34777 	/* Extract the basename */
34778 	while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
34779 		zEnd--;
34780 	}
34781 	zPtr = (zEnd > zPath) ? &zEnd[1] : zPath;
34782 	zEnd = &zPath[nByte];
34783 	/* dirname */
34784 	pCur = &pOut->sDir;
34785 	SyStringInitFromBuf(pCur, zPath, zPtr-zPath);
34786 	if( pCur->nByte > 1 ){
34787 		SyStringTrimTrailingChar(pCur, '/');
34788 #ifdef __WINNT__
34789 		SyStringTrimTrailingChar(pCur, '\\');
34790 #endif
34791 	}else if( (int)zPath[0] == c || (int)zPath[0] == d ){
34792 #ifdef __WINNT__
34793 		SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
34794 #else
34795 		SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
34796 #endif
34797 	}
34798 	/* basename/filename */
34799 	pCur = &pOut->sBasename;
34800 	SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr);
34801 	SyStringTrimLeadingChar(pCur, '/');
34802 #ifdef __WINNT__
34803 	SyStringTrimLeadingChar(pCur, '\\');
34804 #endif
34805 	SyStringDupPtr(&pOut->sFilename, pCur);
34806 	if( pCur->nByte > 0 ){
34807 		/* extension */
34808 		zEnd--;
34809 		while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){
34810 			zEnd--;
34811 		}
34812 		if( zEnd > pCur->zString ){
34813 			zEnd++; /* Jump leading dot */
34814 			SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd);
34815 			/* Fix filename */
34816 			pCur = &pOut->sFilename;
34817 			if( pCur->nByte > SyStringLength(&pOut->sExtension) ){
34818 				pCur->nByte -= 1 + SyStringLength(&pOut->sExtension);
34819 			}
34820 		}
34821 	}
34822 	return SXRET_OK;
34823 }
34824 /*
34825  * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
34826  *  See block comment above.
34827  */
jx9Builtin_pathinfo(jx9_context * pCtx,int nArg,jx9_value ** apArg)34828 static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
34829 {
34830 	const char *zPath;
34831 	path_info sInfo;
34832 	SyString *pComp;
34833 	int iLen;
34834 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
34835 		/* Missing/Invalid argument, return the empty string */
34836 		jx9_result_string(pCtx, "", 0);
34837 		return JX9_OK;
34838 	}
34839 	/* Point to the target path */
34840 	zPath = jx9_value_to_string(apArg[0], &iLen);
34841 	if( iLen < 1 ){
34842 		/* Empty string */
34843 		jx9_result_string(pCtx, "", 0);
34844 		return JX9_OK;
34845 	}
34846 	/* Extract path info */
34847 	ExtractPathInfo(zPath, iLen, &sInfo);
34848 	if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
34849 		/* Return path component */
34850 		int nComp = jx9_value_to_int(apArg[1]);
34851 		switch(nComp){
34852 		case 1: /* PATHINFO_DIRNAME */
34853 			pComp = &sInfo.sDir;
34854 			if( pComp->nByte > 0 ){
34855 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34856 			}else{
34857 				/* Expand the empty string */
34858 				jx9_result_string(pCtx, "", 0);
34859 			}
34860 			break;
34861 		case 2: /*PATHINFO_BASENAME*/
34862 			pComp = &sInfo.sBasename;
34863 			if( pComp->nByte > 0 ){
34864 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34865 			}else{
34866 				/* Expand the empty string */
34867 				jx9_result_string(pCtx, "", 0);
34868 			}
34869 			break;
34870 		case 3: /*PATHINFO_EXTENSION*/
34871 			pComp = &sInfo.sExtension;
34872 			if( pComp->nByte > 0 ){
34873 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34874 			}else{
34875 				/* Expand the empty string */
34876 				jx9_result_string(pCtx, "", 0);
34877 			}
34878 			break;
34879 		case 4: /*PATHINFO_FILENAME*/
34880 			pComp = &sInfo.sFilename;
34881 			if( pComp->nByte > 0 ){
34882 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
34883 			}else{
34884 				/* Expand the empty string */
34885 				jx9_result_string(pCtx, "", 0);
34886 			}
34887 			break;
34888 		default:
34889 			/* Expand the empty string */
34890 			jx9_result_string(pCtx, "", 0);
34891 			break;
34892 		}
34893 	}else{
34894 		/* Return an associative array */
34895 		jx9_value *pArray, *pValue;
34896 		pArray = jx9_context_new_array(pCtx);
34897 		pValue = jx9_context_new_scalar(pCtx);
34898 		if( pArray == 0 || pValue == 0 ){
34899 			/* Out of mem, return NULL */
34900 			jx9_result_bool(pCtx, 0);
34901 			return JX9_OK;
34902 		}
34903 		/* dirname */
34904 		pComp = &sInfo.sDir;
34905 		if( pComp->nByte > 0 ){
34906 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34907 			/* Perform the insertion */
34908 			jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
34909 		}
34910 		/* Reset the string cursor */
34911 		jx9_value_reset_string_cursor(pValue);
34912 		/* basername */
34913 		pComp = &sInfo.sBasename;
34914 		if( pComp->nByte > 0 ){
34915 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34916 			/* Perform the insertion */
34917 			jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
34918 		}
34919 		/* Reset the string cursor */
34920 		jx9_value_reset_string_cursor(pValue);
34921 		/* extension */
34922 		pComp = &sInfo.sExtension;
34923 		if( pComp->nByte > 0 ){
34924 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34925 			/* Perform the insertion */
34926 			jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
34927 		}
34928 		/* Reset the string cursor */
34929 		jx9_value_reset_string_cursor(pValue);
34930 		/* filename */
34931 		pComp = &sInfo.sFilename;
34932 		if( pComp->nByte > 0 ){
34933 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
34934 			/* Perform the insertion */
34935 			jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
34936 		}
34937 		/* Return the created array */
34938 		jx9_result_value(pCtx, pArray);
34939 		/* Don't worry about freeing memory, everything will be released
34940 		 * automatically as soon we return from this foreign function.
34941 		 */
34942 	}
34943 	return JX9_OK;
34944 }
34945 /*
34946  * Globbing implementation extracted from the sqlite3 source tree.
34947  * Original author: D. Richard Hipp (http://www.sqlite.org)
34948  * Status: Public Domain
34949  */
34950 typedef unsigned char u8;
34951 /* An array to map all upper-case characters into their corresponding
34952 ** lower-case character.
34953 **
34954 ** SQLite only considers US-ASCII (or EBCDIC) characters.  We do not
34955 ** handle case conversions for the UTF character set since the tables
34956 ** involved are nearly as big or bigger than SQLite itself.
34957 */
34958 static const unsigned char sqlite3UpperToLower[] = {
34959       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
34960      18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
34961      36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
34962      54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103,
34963     104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
34964     122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
34965     108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
34966     126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
34967     144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
34968     162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
34969     180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
34970     198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
34971     216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
34972     234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
34973     252, 253, 254, 255
34974 };
34975 #define GlogUpperToLower(A)     if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
34976 /*
34977 ** Assuming zIn points to the first byte of a UTF-8 character,
34978 ** advance zIn to point to the first byte of the next UTF-8 character.
34979 */
34980 #define SQLITE_SKIP_UTF8(zIn) {                        \
34981   if( (*(zIn++))>=0xc0 ){                              \
34982     while( (*zIn & 0xc0)==0x80 ){ zIn++; }             \
34983   }                                                    \
34984 }
34985 /*
34986 ** Compare two UTF-8 strings for equality where the first string can
34987 ** potentially be a "glob" expression.  Return true (1) if they
34988 ** are the same and false (0) if they are different.
34989 **
34990 ** Globbing rules:
34991 **
34992 **      '*'       Matches any sequence of zero or more characters.
34993 **
34994 **      '?'       Matches exactly one character.
34995 **
34996 **     [...]      Matches one character from the enclosed list of
34997 **                characters.
34998 **
34999 **     [^...]     Matches one character not in the enclosed list.
35000 **
35001 ** With the [...] and [^...] matching, a ']' character can be included
35002 ** in the list by making it the first character after '[' or '^'.  A
35003 ** range of characters can be specified using '-'.  Example:
35004 ** "[a-z]" matches any single lower-case letter.  To match a '-', make
35005 ** it the last character in the list.
35006 **
35007 ** This routine is usually quick, but can be N**2 in the worst case.
35008 **
35009 ** Hints: to match '*' or '?', put them in "[]".  Like this:
35010 **
35011 **         abc[*]xyz        Matches "abc*xyz" only
35012 */
patternCompare(const u8 * zPattern,const u8 * zString,const int esc,int noCase)35013 static int patternCompare(
35014   const u8 *zPattern,              /* The glob pattern */
35015   const u8 *zString,               /* The string to compare against the glob */
35016   const int esc,                    /* The escape character */
35017   int noCase
35018 ){
35019   int c, c2;
35020   int invert;
35021   int seen;
35022   u8 matchOne = '?';
35023   u8 matchAll = '*';
35024   u8 matchSet = '[';
35025   int prevEscape = 0;     /* True if the previous character was 'escape' */
35026 
35027   if( !zPattern || !zString ) return 0;
35028   while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
35029     if( !prevEscape && c==matchAll ){
35030       while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
35031                || c == matchOne ){
35032         if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
35033           return 0;
35034         }
35035       }
35036       if( c==0 ){
35037         return 1;
35038       }else if( c==esc ){
35039         c = jx9Utf8Read(zPattern, 0, &zPattern);
35040         if( c==0 ){
35041           return 0;
35042         }
35043       }else if( c==matchSet ){
35044 	  if( (esc==0) || (matchSet<0x80) ) return 0;
35045 	  while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){
35046           SQLITE_SKIP_UTF8(zString);
35047         }
35048         return *zString!=0;
35049       }
35050       while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){
35051         if( noCase ){
35052           GlogUpperToLower(c2);
35053           GlogUpperToLower(c);
35054           while( c2 != 0 && c2 != c ){
35055             c2 = jx9Utf8Read(zString, 0, &zString);
35056             GlogUpperToLower(c2);
35057           }
35058         }else{
35059           while( c2 != 0 && c2 != c ){
35060             c2 = jx9Utf8Read(zString, 0, &zString);
35061           }
35062         }
35063         if( c2==0 ) return 0;
35064 		if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
35065       }
35066       return 0;
35067     }else if( !prevEscape && c==matchOne ){
35068       if( jx9Utf8Read(zString, 0, &zString)==0 ){
35069         return 0;
35070       }
35071     }else if( c==matchSet ){
35072       int prior_c = 0;
35073       if( esc == 0 ) return 0;
35074       seen = 0;
35075       invert = 0;
35076       c = jx9Utf8Read(zString, 0, &zString);
35077       if( c==0 ) return 0;
35078       c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35079       if( c2=='^' ){
35080         invert = 1;
35081         c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35082       }
35083       if( c2==']' ){
35084         if( c==']' ) seen = 1;
35085         c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35086       }
35087       while( c2 && c2!=']' ){
35088         if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
35089           c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35090           if( c>=prior_c && c<=c2 ) seen = 1;
35091           prior_c = 0;
35092         }else{
35093           if( c==c2 ){
35094             seen = 1;
35095           }
35096           prior_c = c2;
35097         }
35098         c2 = jx9Utf8Read(zPattern, 0, &zPattern);
35099       }
35100       if( c2==0 || (seen ^ invert)==0 ){
35101         return 0;
35102       }
35103     }else if( esc==c && !prevEscape ){
35104       prevEscape = 1;
35105     }else{
35106       c2 = jx9Utf8Read(zString, 0, &zString);
35107       if( noCase ){
35108         GlogUpperToLower(c);
35109         GlogUpperToLower(c2);
35110       }
35111       if( c!=c2 ){
35112         return 0;
35113       }
35114       prevEscape = 0;
35115     }
35116   }
35117   return *zString==0;
35118 }
35119 /*
35120  * Wrapper around patternCompare() defined above.
35121  * See block comment above for more information.
35122  */
Glob(const unsigned char * zPattern,const unsigned char * zString,int iEsc,int CaseCompare)35123 static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare)
35124 {
35125 	int rc;
35126 	if( iEsc < 0 ){
35127 		iEsc = '\\';
35128 	}
35129 	rc = patternCompare(zPattern, zString, iEsc, CaseCompare);
35130 	return rc;
35131 }
35132 /*
35133  * bool fnmatch(string $pattern, string $string[, int $flags = 0 ])
35134  *  Match filename against a pattern.
35135  * Parameters
35136  *  $pattern
35137  *   The shell wildcard pattern.
35138  * $string
35139  *  The tested string.
35140  * $flags
35141  *   A list of possible flags:
35142  *    FNM_NOESCAPE 	Disable backslash escaping.
35143  *    FNM_PATHNAME 	Slash in string only matches slash in the given pattern.
35144  *    FNM_PERIOD 	Leading period in string must be exactly matched by period in the given pattern.
35145  *    FNM_CASEFOLD 	Caseless match.
35146  * Return
35147  *  TRUE if there is a match, FALSE otherwise.
35148  */
jx9Builtin_fnmatch(jx9_context * pCtx,int nArg,jx9_value ** apArg)35149 static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
35150 {
35151 	const char *zString, *zPattern;
35152 	int iEsc = '\\';
35153 	int noCase = 0;
35154 	int rc;
35155 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35156 		/* Missing/Invalid arguments, return FALSE */
35157 		jx9_result_bool(pCtx, 0);
35158 		return JX9_OK;
35159 	}
35160 	/* Extract the pattern and the string */
35161 	zPattern  = jx9_value_to_string(apArg[0], 0);
35162 	zString = jx9_value_to_string(apArg[1], 0);
35163 	/* Extract the flags if avaialble */
35164 	if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
35165 		rc = jx9_value_to_int(apArg[2]);
35166 		if( rc & 0x01 /*FNM_NOESCAPE*/){
35167 			iEsc = 0;
35168 		}
35169 		if( rc & 0x08 /*FNM_CASEFOLD*/){
35170 			noCase = 1;
35171 		}
35172 	}
35173 	/* Go globbing */
35174 	rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase);
35175 	/* Globbing result */
35176 	jx9_result_bool(pCtx, rc);
35177 	return JX9_OK;
35178 }
35179 /*
35180  * bool strglob(string $pattern, string $string)
35181  *  Match string against a pattern.
35182  * Parameters
35183  *  $pattern
35184  *   The shell wildcard pattern.
35185  * $string
35186  *  The tested string.
35187  * Return
35188  *  TRUE if there is a match, FALSE otherwise.
35189  */
jx9Builtin_strglob(jx9_context * pCtx,int nArg,jx9_value ** apArg)35190 static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
35191 {
35192 	const char *zString, *zPattern;
35193 	int iEsc = '\\';
35194 	int rc;
35195 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35196 		/* Missing/Invalid arguments, return FALSE */
35197 		jx9_result_bool(pCtx, 0);
35198 		return JX9_OK;
35199 	}
35200 	/* Extract the pattern and the string */
35201 	zPattern  = jx9_value_to_string(apArg[0], 0);
35202 	zString = jx9_value_to_string(apArg[1], 0);
35203 	/* Go globbing */
35204 	rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
35205 	/* Globbing result */
35206 	jx9_result_bool(pCtx, rc);
35207 	return JX9_OK;
35208 }
35209 /*
35210  * bool link(string $target, string $link)
35211  *  Create a hard link.
35212  * Parameters
35213  *  $target
35214  *   Target of the link.
35215  *  $link
35216  *   The link name.
35217  * Return
35218  *  TRUE on success or FALSE on failure.
35219  */
jx9Vfs_link(jx9_context * pCtx,int nArg,jx9_value ** apArg)35220 static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
35221 {
35222 	const char *zTarget, *zLink;
35223 	jx9_vfs *pVfs;
35224 	int rc;
35225 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35226 		/* Missing/Invalid arguments, return FALSE */
35227 		jx9_result_bool(pCtx, 0);
35228 		return JX9_OK;
35229 	}
35230 	/* Point to the underlying vfs */
35231 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35232 	if( pVfs == 0 || pVfs->xLink == 0 ){
35233 		/* IO routine not implemented, return NULL */
35234 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35235 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
35236 			jx9_function_name(pCtx)
35237 			);
35238 		jx9_result_bool(pCtx, 0);
35239 		return JX9_OK;
35240 	}
35241 	/* Extract the given arguments */
35242 	zTarget  = jx9_value_to_string(apArg[0], 0);
35243 	zLink = jx9_value_to_string(apArg[1], 0);
35244 	/* Perform the requested operation */
35245 	rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
35246 	/* IO result */
35247 	jx9_result_bool(pCtx, rc == JX9_OK );
35248 	return JX9_OK;
35249 }
35250 /*
35251  * bool symlink(string $target, string $link)
35252  *  Creates a symbolic link.
35253  * Parameters
35254  *  $target
35255  *   Target of the link.
35256  *  $link
35257  *   The link name.
35258  * Return
35259  *  TRUE on success or FALSE on failure.
35260  */
jx9Vfs_symlink(jx9_context * pCtx,int nArg,jx9_value ** apArg)35261 static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
35262 {
35263 	const char *zTarget, *zLink;
35264 	jx9_vfs *pVfs;
35265 	int rc;
35266 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
35267 		/* Missing/Invalid arguments, return FALSE */
35268 		jx9_result_bool(pCtx, 0);
35269 		return JX9_OK;
35270 	}
35271 	/* Point to the underlying vfs */
35272 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35273 	if( pVfs == 0 || pVfs->xLink == 0 ){
35274 		/* IO routine not implemented, return NULL */
35275 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35276 			"IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
35277 			jx9_function_name(pCtx)
35278 			);
35279 		jx9_result_bool(pCtx, 0);
35280 		return JX9_OK;
35281 	}
35282 	/* Extract the given arguments */
35283 	zTarget  = jx9_value_to_string(apArg[0], 0);
35284 	zLink = jx9_value_to_string(apArg[1], 0);
35285 	/* Perform the requested operation */
35286 	rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
35287 	/* IO result */
35288 	jx9_result_bool(pCtx, rc == JX9_OK );
35289 	return JX9_OK;
35290 }
35291 /*
35292  * int umask([ int $mask ])
35293  *  Changes the current umask.
35294  * Parameters
35295  *  $mask
35296  *   The new umask.
35297  * Return
35298  *  umask() without arguments simply returns the current umask.
35299  *  Otherwise the old umask is returned.
35300  */
jx9Vfs_umask(jx9_context * pCtx,int nArg,jx9_value ** apArg)35301 static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
35302 {
35303 	int iOld, iNew;
35304 	jx9_vfs *pVfs;
35305 	/* Point to the underlying vfs */
35306 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35307 	if( pVfs == 0 || pVfs->xUmask == 0 ){
35308 		/* IO routine not implemented, return -1 */
35309 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35310 			"IO routine(%s) not implemented in the underlying VFS",
35311 			jx9_function_name(pCtx)
35312 			);
35313 		jx9_result_int(pCtx, 0);
35314 		return JX9_OK;
35315 	}
35316 	iNew = 0;
35317 	if( nArg > 0 ){
35318 		iNew = jx9_value_to_int(apArg[0]);
35319 	}
35320 	/* Perform the requested operation */
35321 	iOld = pVfs->xUmask(iNew);
35322 	/* Old mask */
35323 	jx9_result_int(pCtx, iOld);
35324 	return JX9_OK;
35325 }
35326 /*
35327  * string sys_get_temp_dir()
35328  *  Returns directory path used for temporary files.
35329  * Parameters
35330  *  None
35331  * Return
35332  *  Returns the path of the temporary directory.
35333  */
jx9Vfs_sys_get_temp_dir(jx9_context * pCtx,int nArg,jx9_value ** apArg)35334 static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
35335 {
35336 	jx9_vfs *pVfs;
35337 	/* Set the empty string as the default return value */
35338 	jx9_result_string(pCtx, "", 0);
35339 	/* Point to the underlying vfs */
35340 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35341 	if( pVfs == 0 || pVfs->xTempDir == 0 ){
35342 		SXUNUSED(nArg); /* cc warning */
35343 		SXUNUSED(apArg);
35344 		/* IO routine not implemented, return "" */
35345 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35346 			"IO routine(%s) not implemented in the underlying VFS",
35347 			jx9_function_name(pCtx)
35348 			);
35349 		return JX9_OK;
35350 	}
35351 	/* Perform the requested operation */
35352 	pVfs->xTempDir(pCtx);
35353 	return JX9_OK;
35354 }
35355 /*
35356  * string get_current_user()
35357  *  Returns the name of the current working user.
35358  * Parameters
35359  *  None
35360  * Return
35361  *  Returns the name of the current working user.
35362  */
jx9Vfs_get_current_user(jx9_context * pCtx,int nArg,jx9_value ** apArg)35363 static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
35364 {
35365 	jx9_vfs *pVfs;
35366 	/* Point to the underlying vfs */
35367 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35368 	if( pVfs == 0 || pVfs->xUsername == 0 ){
35369 		SXUNUSED(nArg); /* cc warning */
35370 		SXUNUSED(apArg);
35371 		/* IO routine not implemented */
35372 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35373 			"IO routine(%s) not implemented in the underlying VFS",
35374 			jx9_function_name(pCtx)
35375 			);
35376 		/* Set a dummy username */
35377 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
35378 		return JX9_OK;
35379 	}
35380 	/* Perform the requested operation */
35381 	pVfs->xUsername(pCtx);
35382 	return JX9_OK;
35383 }
35384 /*
35385  * int64 getmypid()
35386  *  Gets process ID.
35387  * Parameters
35388  *  None
35389  * Return
35390  *  Returns the process ID.
35391  */
jx9Vfs_getmypid(jx9_context * pCtx,int nArg,jx9_value ** apArg)35392 static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35393 {
35394 	jx9_int64 nProcessId;
35395 	jx9_vfs *pVfs;
35396 	/* Point to the underlying vfs */
35397 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35398 	if( pVfs == 0 || pVfs->xProcessId == 0 ){
35399 		SXUNUSED(nArg); /* cc warning */
35400 		SXUNUSED(apArg);
35401 		/* IO routine not implemented, return -1 */
35402 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35403 			"IO routine(%s) not implemented in the underlying VFS",
35404 			jx9_function_name(pCtx)
35405 			);
35406 		jx9_result_int(pCtx, -1);
35407 		return JX9_OK;
35408 	}
35409 	/* Perform the requested operation */
35410 	nProcessId = (jx9_int64)pVfs->xProcessId();
35411 	/* Set the result */
35412 	jx9_result_int64(pCtx, nProcessId);
35413 	return JX9_OK;
35414 }
35415 /*
35416  * int getmyuid()
35417  *  Get user ID.
35418  * Parameters
35419  *  None
35420  * Return
35421  *  Returns the user ID.
35422  */
jx9Vfs_getmyuid(jx9_context * pCtx,int nArg,jx9_value ** apArg)35423 static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35424 {
35425 	jx9_vfs *pVfs;
35426 	int nUid;
35427 	/* Point to the underlying vfs */
35428 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35429 	if( pVfs == 0 || pVfs->xUid == 0 ){
35430 		SXUNUSED(nArg); /* cc warning */
35431 		SXUNUSED(apArg);
35432 		/* IO routine not implemented, return -1 */
35433 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35434 			"IO routine(%s) not implemented in the underlying VFS",
35435 			jx9_function_name(pCtx)
35436 			);
35437 		jx9_result_int(pCtx, -1);
35438 		return JX9_OK;
35439 	}
35440 	/* Perform the requested operation */
35441 	nUid = pVfs->xUid();
35442 	/* Set the result */
35443 	jx9_result_int(pCtx, nUid);
35444 	return JX9_OK;
35445 }
35446 /*
35447  * int getmygid()
35448  *  Get group ID.
35449  * Parameters
35450  *  None
35451  * Return
35452  *  Returns the group ID.
35453  */
jx9Vfs_getmygid(jx9_context * pCtx,int nArg,jx9_value ** apArg)35454 static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
35455 {
35456 	jx9_vfs *pVfs;
35457 	int nGid;
35458 	/* Point to the underlying vfs */
35459 	pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
35460 	if( pVfs == 0 || pVfs->xGid == 0 ){
35461 		SXUNUSED(nArg); /* cc warning */
35462 		SXUNUSED(apArg);
35463 		/* IO routine not implemented, return -1 */
35464 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35465 			"IO routine(%s) not implemented in the underlying VFS",
35466 			jx9_function_name(pCtx)
35467 			);
35468 		jx9_result_int(pCtx, -1);
35469 		return JX9_OK;
35470 	}
35471 	/* Perform the requested operation */
35472 	nGid = pVfs->xGid();
35473 	/* Set the result */
35474 	jx9_result_int(pCtx, nGid);
35475 	return JX9_OK;
35476 }
35477 #ifdef __WINNT__
35478 #include <Windows.h>
35479 #elif defined(__UNIXES__)
35480 #include <sys/utsname.h>
35481 #endif
35482 /*
35483  * string uname([ string $mode = "a" ])
35484  *  Returns information about the host operating system.
35485  * Parameters
35486  *  $mode
35487  *   mode is a single character that defines what information is returned:
35488  *    'a': This is the default. Contains all modes in the sequence "s n r v m".
35489  *    's': Operating system name. eg. FreeBSD.
35490  *    'n': Host name. eg. localhost.example.com.
35491  *    'r': Release name. eg. 5.1.2-RELEASE.
35492  *    'v': Version information. Varies a lot between operating systems.
35493  *    'm': Machine type. eg. i386.
35494  * Return
35495  *  OS description as a string.
35496  */
jx9Vfs_uname(jx9_context * pCtx,int nArg,jx9_value ** apArg)35497 static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg)
35498 {
35499 #if defined(__WINNT__)
35500 	const char *zName = "Microsoft Windows";
35501 	OSVERSIONINFOW sVer;
35502 #elif defined(__UNIXES__)
35503 	struct utsname sName;
35504 #endif
35505 	const char *zMode = "a";
35506 	if( nArg > 0 && jx9_value_is_string(apArg[0]) ){
35507 		/* Extract the desired mode */
35508 		zMode = jx9_value_to_string(apArg[0], 0);
35509 	}
35510 #if defined(__WINNT__)
35511 	sVer.dwOSVersionInfoSize = sizeof(sVer);
35512 	if( TRUE != GetVersionExW(&sVer)){
35513 		jx9_result_string(pCtx, zName, -1);
35514 		return JX9_OK;
35515 	}
35516 	if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){
35517 		if( sVer.dwMajorVersion <= 4 ){
35518 			zName = "Microsoft Windows NT";
35519 		}else if( sVer.dwMajorVersion == 5 ){
35520 			switch(sVer.dwMinorVersion){
35521 				case 0:	zName = "Microsoft Windows 2000"; break;
35522 				case 1: zName = "Microsoft Windows XP";   break;
35523 				case 2: zName = "Microsoft Windows Server 2003"; break;
35524 			}
35525 		}else if( sVer.dwMajorVersion == 6){
35526 				switch(sVer.dwMinorVersion){
35527 					case 0: zName = "Microsoft Windows Vista"; break;
35528 					case 1: zName = "Microsoft Windows 7"; break;
35529 					case 2: zName = "Microsoft Windows 8"; break;
35530 					default: break;
35531 				}
35532 		}
35533 	}
35534 	switch(zMode[0]){
35535 	case 's':
35536 		/* Operating system name */
35537 		jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
35538 		break;
35539 	case 'n':
35540 		/* Host name */
35541 		jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
35542 		break;
35543 	case 'r':
35544 	case 'v':
35545 		/* Version information. */
35546 		jx9_result_string_format(pCtx, "%u.%u build %u",
35547 			sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
35548 			);
35549 		break;
35550 	case 'm':
35551 		/* Machine name */
35552 		jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
35553 		break;
35554 	default:
35555 		jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86",
35556 			zName,
35557 			sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
35558 			);
35559 		break;
35560 	}
35561 #elif defined(__UNIXES__)
35562 	if( uname(&sName) != 0 ){
35563 		jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
35564 		return JX9_OK;
35565 	}
35566 	switch(zMode[0]){
35567 	case 's':
35568 		/* Operating system name */
35569 		jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
35570 		break;
35571 	case 'n':
35572 		/* Host name */
35573 		jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
35574 		break;
35575 	case 'r':
35576 		/* Release information */
35577 		jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
35578 		break;
35579 	case 'v':
35580 		/* Version information. */
35581 		jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
35582 		break;
35583 	case 'm':
35584 		/* Machine name */
35585 		jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
35586 		break;
35587 	default:
35588 		jx9_result_string_format(pCtx,
35589 			"%s %s %s %s %s",
35590 			sName.sysname,
35591 			sName.release,
35592 			sName.version,
35593 			sName.nodename,
35594 			sName.machine
35595 			);
35596 		break;
35597 	}
35598 #else
35599 	jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
35600 #endif
35601 	return JX9_OK;
35602 }
35603 /*
35604  * Section:
35605  *    IO stream implementation.
35606  * Authors:
35607  *    Symisc Systems, devel@symisc.net.
35608  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
35609  * Status:
35610  *    Stable.
35611  */
35612 typedef struct io_private io_private;
35613 struct io_private
35614 {
35615 	const jx9_io_stream *pStream; /* Underlying IO device */
35616 	void *pHandle; /* IO handle */
35617 	/* Unbuffered IO */
35618 	SyBlob sBuffer; /* Working buffer */
35619 	sxu32 nOfft;    /* Current read offset */
35620 	sxu32 iMagic;   /* Sanity check to avoid misuse */
35621 };
35622 #define IO_PRIVATE_MAGIC 0xFEAC14
35623 /* Make sure we are dealing with a valid io_private instance */
35624 #define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC )
35625 /* Forward declaration */
35626 static void ResetIOPrivate(io_private *pDev);
35627 /*
35628  * bool ftruncate(resource $handle, int64 $size)
35629  *  Truncates a file to a given length.
35630  * Parameters
35631  *  $handle
35632  *   The file pointer.
35633  *   Note:
35634  *    The handle must be open for writing.
35635  * $size
35636  *   The size to truncate to.
35637  * Return
35638  *  TRUE on success or FALSE on failure.
35639  */
jx9Builtin_ftruncate(jx9_context * pCtx,int nArg,jx9_value ** apArg)35640 static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
35641 {
35642 	const jx9_io_stream *pStream;
35643 	io_private *pDev;
35644 	int rc;
35645 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
35646 		/* Missing/Invalid arguments, return FALSE */
35647 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35648 		jx9_result_bool(pCtx, 0);
35649 		return JX9_OK;
35650 	}
35651 	/* Extract our private data */
35652 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35653 	/* Make sure we are dealing with a valid io_private instance */
35654 	if( IO_PRIVATE_INVALID(pDev) ){
35655 		/*Expecting an IO handle */
35656 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35657 		jx9_result_bool(pCtx, 0);
35658 		return JX9_OK;
35659 	}
35660 	/* Point to the target IO stream device */
35661 	pStream = pDev->pStream;
35662 	if( pStream == 0  || pStream->xTrunc == 0){
35663 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35664 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35665 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35666 			);
35667 		jx9_result_bool(pCtx, 0);
35668 		return JX9_OK;
35669 	}
35670 	/* Perform the requested operation */
35671 	rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
35672 	if( rc == JX9_OK ){
35673 		/* Discard buffered data */
35674 		ResetIOPrivate(pDev);
35675 	}
35676 	/* IO result */
35677 	jx9_result_bool(pCtx, rc == JX9_OK);
35678 	return JX9_OK;
35679 }
35680 /*
35681  * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ])
35682  *  Seeks on a file pointer.
35683  * Parameters
35684  *  $handle
35685  *   A file system pointer resource that is typically created using fopen().
35686  * $offset
35687  *   The offset.
35688  *   To move to a position before the end-of-file, you need to pass a negative
35689  *   value in offset and set whence to SEEK_END.
35690  *   whence
35691  *   whence values are:
35692  *    SEEK_SET - Set position equal to offset bytes.
35693  *    SEEK_CUR - Set position to current location plus offset.
35694  *    SEEK_END - Set position to end-of-file plus offset.
35695  * Return
35696  *  0 on success, -1 on failure
35697  */
jx9Builtin_fseek(jx9_context * pCtx,int nArg,jx9_value ** apArg)35698 static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
35699 {
35700 	const jx9_io_stream *pStream;
35701 	io_private *pDev;
35702 	jx9_int64 iOfft;
35703 	int whence;
35704 	int rc;
35705 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
35706 		/* Missing/Invalid arguments, return FALSE */
35707 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35708 		jx9_result_int(pCtx, -1);
35709 		return JX9_OK;
35710 	}
35711 	/* Extract our private data */
35712 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35713 	/* Make sure we are dealing with a valid io_private instance */
35714 	if( IO_PRIVATE_INVALID(pDev) ){
35715 		/*Expecting an IO handle */
35716 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35717 		jx9_result_int(pCtx, -1);
35718 		return JX9_OK;
35719 	}
35720 	/* Point to the target IO stream device */
35721 	pStream = pDev->pStream;
35722 	if( pStream == 0  || pStream->xSeek == 0){
35723 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35724 			"IO routine(%s) not implemented in the underlying stream(%s) device",
35725 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35726 			);
35727 		jx9_result_int(pCtx, -1);
35728 		return JX9_OK;
35729 	}
35730 	/* Extract the offset */
35731 	iOfft = jx9_value_to_int64(apArg[1]);
35732 	whence = 0;/* SEEK_SET */
35733 	if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
35734 		whence = jx9_value_to_int(apArg[2]);
35735 	}
35736 	/* Perform the requested operation */
35737 	rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
35738 	if( rc == JX9_OK ){
35739 		/* Ignore buffered data */
35740 		ResetIOPrivate(pDev);
35741 	}
35742 	/* IO result */
35743 	jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
35744 	return JX9_OK;
35745 }
35746 /*
35747  * int64 ftell(resource $handle)
35748  *  Returns the current position of the file read/write pointer.
35749  * Parameters
35750  *  $handle
35751  *   The file pointer.
35752  * Return
35753  *  Returns the position of the file pointer referenced by handle
35754  *  as an integer; i.e., its offset into the file stream.
35755  *  FALSE is returned on failure.
35756  */
jx9Builtin_ftell(jx9_context * pCtx,int nArg,jx9_value ** apArg)35757 static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
35758 {
35759 	const jx9_io_stream *pStream;
35760 	io_private *pDev;
35761 	jx9_int64 iOfft;
35762 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35763 		/* Missing/Invalid arguments, return FALSE */
35764 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35765 		jx9_result_bool(pCtx, 0);
35766 		return JX9_OK;
35767 	}
35768 	/* Extract our private data */
35769 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35770 	/* Make sure we are dealing with a valid io_private instance */
35771 	if( IO_PRIVATE_INVALID(pDev) ){
35772 		/*Expecting an IO handle */
35773 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35774 		jx9_result_bool(pCtx, 0);
35775 		return JX9_OK;
35776 	}
35777 	/* Point to the target IO stream device */
35778 	pStream = pDev->pStream;
35779 	if( pStream == 0  || pStream->xTell == 0){
35780 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35781 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35782 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35783 			);
35784 		jx9_result_bool(pCtx, 0);
35785 		return JX9_OK;
35786 	}
35787 	/* Perform the requested operation */
35788 	iOfft = pStream->xTell(pDev->pHandle);
35789 	/* IO result */
35790 	jx9_result_int64(pCtx, iOfft);
35791 	return JX9_OK;
35792 }
35793 /*
35794  * bool rewind(resource $handle)
35795  *  Rewind the position of a file pointer.
35796  * Parameters
35797  *  $handle
35798  *   The file pointer.
35799  * Return
35800  *  TRUE on success or FALSE on failure.
35801  */
jx9Builtin_rewind(jx9_context * pCtx,int nArg,jx9_value ** apArg)35802 static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
35803 {
35804 	const jx9_io_stream *pStream;
35805 	io_private *pDev;
35806 	int rc;
35807 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35808 		/* Missing/Invalid arguments, return FALSE */
35809 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35810 		jx9_result_bool(pCtx, 0);
35811 		return JX9_OK;
35812 	}
35813 	/* Extract our private data */
35814 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35815 	/* Make sure we are dealing with a valid io_private instance */
35816 	if( IO_PRIVATE_INVALID(pDev) ){
35817 		/*Expecting an IO handle */
35818 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35819 		jx9_result_bool(pCtx, 0);
35820 		return JX9_OK;
35821 	}
35822 	/* Point to the target IO stream device */
35823 	pStream = pDev->pStream;
35824 	if( pStream == 0  || pStream->xSeek == 0){
35825 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35826 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35827 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35828 			);
35829 		jx9_result_bool(pCtx, 0);
35830 		return JX9_OK;
35831 	}
35832 	/* Perform the requested operation */
35833 	rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
35834 	if( rc == JX9_OK ){
35835 		/* Ignore buffered data */
35836 		ResetIOPrivate(pDev);
35837 	}
35838 	/* IO result */
35839 	jx9_result_bool(pCtx, rc == JX9_OK);
35840 	return JX9_OK;
35841 }
35842 /*
35843  * bool fflush(resource $handle)
35844  *  Flushes the output to a file.
35845  * Parameters
35846  *  $handle
35847  *   The file pointer.
35848  * Return
35849  *  TRUE on success or FALSE on failure.
35850  */
jx9Builtin_fflush(jx9_context * pCtx,int nArg,jx9_value ** apArg)35851 static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
35852 {
35853 	const jx9_io_stream *pStream;
35854 	io_private *pDev;
35855 	int rc;
35856 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35857 		/* Missing/Invalid arguments, return FALSE */
35858 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35859 		jx9_result_bool(pCtx, 0);
35860 		return JX9_OK;
35861 	}
35862 	/* Extract our private data */
35863 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35864 	/* Make sure we are dealing with a valid io_private instance */
35865 	if( IO_PRIVATE_INVALID(pDev) ){
35866 		/*Expecting an IO handle */
35867 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35868 		jx9_result_bool(pCtx, 0);
35869 		return JX9_OK;
35870 	}
35871 	/* Point to the target IO stream device */
35872 	pStream = pDev->pStream;
35873 	if( pStream == 0 || pStream->xSync == 0){
35874 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35875 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35876 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35877 			);
35878 		jx9_result_bool(pCtx, 0);
35879 		return JX9_OK;
35880 	}
35881 	/* Perform the requested operation */
35882 	rc = pStream->xSync(pDev->pHandle);
35883 	/* IO result */
35884 	jx9_result_bool(pCtx, rc == JX9_OK);
35885 	return JX9_OK;
35886 }
35887 /*
35888  * bool feof(resource $handle)
35889  *  Tests for end-of-file on a file pointer.
35890  * Parameters
35891  *  $handle
35892  *   The file pointer.
35893  * Return
35894  *  Returns TRUE if the file pointer is at EOF.FALSE otherwise
35895  */
jx9Builtin_feof(jx9_context * pCtx,int nArg,jx9_value ** apArg)35896 static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
35897 {
35898 	const jx9_io_stream *pStream;
35899 	io_private *pDev;
35900 	int rc;
35901 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
35902 		/* Missing/Invalid arguments */
35903 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35904 		jx9_result_bool(pCtx, 1);
35905 		return JX9_OK;
35906 	}
35907 	/* Extract our private data */
35908 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
35909 	/* Make sure we are dealing with a valid io_private instance */
35910 	if( IO_PRIVATE_INVALID(pDev) ){
35911 		/*Expecting an IO handle */
35912 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
35913 		jx9_result_bool(pCtx, 1);
35914 		return JX9_OK;
35915 	}
35916 	/* Point to the target IO stream device */
35917 	pStream = pDev->pStream;
35918 	if( pStream == 0 ){
35919 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
35920 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
35921 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
35922 			);
35923 		jx9_result_bool(pCtx, 1);
35924 		return JX9_OK;
35925 	}
35926 	rc = SXERR_EOF;
35927 	/* Perform the requested operation */
35928 	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
35929 		/* Data is available */
35930 		rc = JX9_OK;
35931 	}else{
35932 		char zBuf[4096];
35933 		jx9_int64 n;
35934 		/* Perform a buffered read */
35935 		n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf));
35936 		if( n > 0 ){
35937 			/* Copy buffered data */
35938 			SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
35939 			rc = JX9_OK;
35940 		}
35941 	}
35942 	/* EOF or not */
35943 	jx9_result_bool(pCtx, rc == SXERR_EOF);
35944 	return JX9_OK;
35945 }
35946 /*
35947  * Read n bytes from the underlying IO stream device.
35948  * Return total numbers of bytes readen on success. A number < 1 on failure
35949  * [i.e: IO error ] or EOF.
35950  */
StreamRead(io_private * pDev,void * pBuf,jx9_int64 nLen)35951 static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
35952 {
35953 	const jx9_io_stream *pStream = pDev->pStream;
35954 	char *zBuf = (char *)pBuf;
35955 	jx9_int64 n, nRead;
35956 	n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
35957 	if( n > 0 ){
35958 		if( n > nLen ){
35959 			n = nLen;
35960 		}
35961 		/* Copy the buffered data */
35962 		SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n);
35963 		/* Update the read offset */
35964 		pDev->nOfft += (sxu32)n;
35965 		if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
35966 			/* Reset the working buffer so that we avoid excessive memory allocation */
35967 			SyBlobReset(&pDev->sBuffer);
35968 			pDev->nOfft = 0;
35969 		}
35970 		nLen -= n;
35971 		if( nLen < 1 ){
35972 			/* All done */
35973 			return n;
35974 		}
35975 		/* Advance the cursor */
35976 		zBuf += n;
35977 	}
35978 	/* Read without buffering */
35979 	nRead = pStream->xRead(pDev->pHandle, zBuf, nLen);
35980 	if( nRead > 0 ){
35981 		n += nRead;
35982 	}else if( n < 1 ){
35983 		/* EOF or IO error */
35984 		return nRead;
35985 	}
35986 	return n;
35987 }
35988 /*
35989  * Extract a single line from the buffered input.
35990  */
GetLine(io_private * pDev,jx9_int64 * pLen,const char ** pzLine)35991 static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine)
35992 {
35993 	const char *zIn, *zEnd, *zPtr;
35994 	zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
35995 	zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft];
35996 	zPtr = zIn;
35997 	while( zIn < zEnd ){
35998 		if( zIn[0] == '\n' ){
35999 			/* Line found */
36000 			zIn++; /* Include the line ending as requested by the JX9 specification */
36001 			*pLen = (jx9_int64)(zIn-zPtr);
36002 			*pzLine = zPtr;
36003 			return SXRET_OK;
36004 		}
36005 		zIn++;
36006 	}
36007 	/* No line were found */
36008 	return SXERR_NOTFOUND;
36009 }
36010 /*
36011  * Read a single line from the underlying IO stream device.
36012  */
StreamReadLine(io_private * pDev,const char ** pzData,jx9_int64 nMaxLen)36013 static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
36014 {
36015 	const jx9_io_stream *pStream = pDev->pStream;
36016 	char zBuf[8192];
36017 	jx9_int64 n;
36018 	sxi32 rc;
36019 	n = 0;
36020 	if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
36021 		/* Reset the working buffer so that we avoid excessive memory allocation */
36022 		SyBlobReset(&pDev->sBuffer);
36023 		pDev->nOfft = 0;
36024 	}
36025 	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
36026 		/* Check if there is a line */
36027 		rc = GetLine(pDev, &n, pzData);
36028 		if( rc == SXRET_OK ){
36029 			/* Got line, update the cursor  */
36030 			pDev->nOfft += (sxu32)n;
36031 			return n;
36032 		}
36033 	}
36034 	/* Perform the read operation until a new line is extracted or length
36035 	 * limit is reached.
36036 	 */
36037 	for(;;){
36038 		n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf));
36039 		if( n < 1 ){
36040 			/* EOF or IO error */
36041 			break;
36042 		}
36043 		/* Append the data just read */
36044 		SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
36045 		/* Try to extract a line */
36046 		rc = GetLine(pDev, &n, pzData);
36047 		if( rc == SXRET_OK ){
36048 			/* Got one, return immediately */
36049 			pDev->nOfft += (sxu32)n;
36050 			return n;
36051 		}
36052 		if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){
36053 			/* Read limit reached, return the available data */
36054 			*pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
36055 			n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
36056 			/* Reset the working buffer */
36057 			SyBlobReset(&pDev->sBuffer);
36058 			pDev->nOfft = 0;
36059 			return n;
36060 		}
36061 	}
36062 	if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
36063 		/* Read limit reached, return the available data */
36064 		*pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
36065 		n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
36066 		/* Reset the working buffer */
36067 		SyBlobReset(&pDev->sBuffer);
36068 		pDev->nOfft = 0;
36069 	}
36070 	return n;
36071 }
36072 /*
36073  * Open an IO stream handle.
36074  * Notes on stream:
36075  * According to the JX9 reference manual.
36076  * In its simplest definition, a stream is a resource object which exhibits streamable behavior.
36077  * That is, it can be read from or written to in a linear fashion, and may be able to fseek()
36078  * to an arbitrary locations within the stream.
36079  * A wrapper is additional code which tells the stream how to handle specific protocols/encodings.
36080  * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file
36081  * on a remote server.
36082  * A stream is referenced as: scheme://target
36083  *   scheme(string) - The name of the wrapper to be used. Examples include: file, http...
36084  *   If no wrapper is specified, the function default is used (typically file://).
36085  *   target - Depends on the wrapper used. For filesystem related streams this is typically a path
36086  *  and filename of the desired file. For network related streams this is typically a hostname, often
36087  *  with a path appended.
36088  *
36089  * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately.
36090  * Please refer to the official documentation for a full discussion.
36091  * This function return a handle on success. Otherwise null.
36092  */
jx9StreamOpenHandle(jx9_vm * pVm,const jx9_io_stream * pStream,const char * zFile,int iFlags,int use_include,jx9_value * pResource,int bPushInclude,int * pNew)36093 JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
36094 	int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew)
36095 {
36096 	void *pHandle = 0; /* cc warning */
36097 	SyString sFile;
36098 	int rc;
36099 	if( pStream == 0 ){
36100 		/* No such stream device */
36101 		return 0;
36102 	}
36103 	SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
36104 	if( use_include ){
36105 		if(	sFile.zString[0] == '/' ||
36106 #ifdef __WINNT__
36107 			(sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) ||
36108 #endif
36109 			(sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') ||
36110 			(sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){
36111 				/*  Open the file directly */
36112 				rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
36113 		}else{
36114 			SyString *pPath;
36115 			SyBlob sWorker;
36116 #ifdef __WINNT__
36117 			static const int c = '\\';
36118 #else
36119 			static const int c = '/';
36120 #endif
36121 			/* Init the path builder working buffer */
36122 			SyBlobInit(&sWorker, &pVm->sAllocator);
36123 			/* Build a path from the set of include path */
36124 			SySetResetCursor(&pVm->aPaths);
36125 			rc = SXERR_IO;
36126 			while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){
36127 				/* Build full path */
36128 				SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile);
36129 				/* Append null terminator */
36130 				if( SXRET_OK != SyBlobNullAppend(&sWorker) ){
36131 					continue;
36132 				}
36133 				/* Try to open the file */
36134 				rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle);
36135 				if( rc == JX9_OK ){
36136 					if( bPushInclude ){
36137 						/* Mark as included */
36138 						jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
36139 					}
36140 					break;
36141 				}
36142 				/* Reset the working buffer */
36143 				SyBlobReset(&sWorker);
36144 				/* Check the next path */
36145 			}
36146 			SyBlobRelease(&sWorker);
36147 		}
36148 		if( rc == JX9_OK ){
36149 			if( bPushInclude ){
36150 				/* Mark as included */
36151 				jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
36152 			}
36153 		}
36154 	}else{
36155 		/* Open the URI direcly */
36156 		rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
36157 	}
36158 	if( rc != JX9_OK ){
36159 		/* IO error */
36160 		return 0;
36161 	}
36162 	/* Return the file handle */
36163 	return pHandle;
36164 }
36165 /*
36166  * Read the whole contents of an open IO stream handle [i.e local file/URL..]
36167  * Store the read data in the given BLOB (last argument).
36168  * The read operation is stopped when he hit the EOF or an IO error occurs.
36169  */
jx9StreamReadWholeFile(void * pHandle,const jx9_io_stream * pStream,SyBlob * pOut)36170 JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
36171 {
36172 	jx9_int64 nRead;
36173 	char zBuf[8192]; /* 8K */
36174 	int rc;
36175 	/* Perform the requested operation */
36176 	for(;;){
36177 		nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
36178 		if( nRead < 1 ){
36179 			/* EOF or IO error */
36180 			break;
36181 		}
36182 		/* Append contents */
36183 		rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead);
36184 		if( rc != SXRET_OK ){
36185 			break;
36186 		}
36187 	}
36188 	return SyBlobLength(pOut) > 0 ? SXRET_OK : -1;
36189 }
36190 /*
36191  * Close an open IO stream handle [i.e local file/URI..].
36192  */
jx9StreamCloseHandle(const jx9_io_stream * pStream,void * pHandle)36193 JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle)
36194 {
36195 	if( pStream->xClose ){
36196 		pStream->xClose(pHandle);
36197 	}
36198 }
36199 /*
36200  * string fgetc(resource $handle)
36201  *  Gets a character from the given file pointer.
36202  * Parameters
36203  *  $handle
36204  *   The file pointer.
36205  * Return
36206  *  Returns a string containing a single character read from the file
36207  *  pointed to by handle. Returns FALSE on EOF.
36208  * WARNING
36209  *  This operation is extremely slow.Avoid using it.
36210  */
jx9Builtin_fgetc(jx9_context * pCtx,int nArg,jx9_value ** apArg)36211 static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
36212 {
36213 	const jx9_io_stream *pStream;
36214 	io_private *pDev;
36215 	int c, n;
36216 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36217 		/* Missing/Invalid arguments, return FALSE */
36218 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36219 		jx9_result_bool(pCtx, 0);
36220 		return JX9_OK;
36221 	}
36222 	/* Extract our private data */
36223 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36224 	/* Make sure we are dealing with a valid io_private instance */
36225 	if( IO_PRIVATE_INVALID(pDev) ){
36226 		/*Expecting an IO handle */
36227 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36228 		jx9_result_bool(pCtx, 0);
36229 		return JX9_OK;
36230 	}
36231 	/* Point to the target IO stream device */
36232 	pStream = pDev->pStream;
36233 	if( pStream == 0  ){
36234 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36235 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36236 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36237 			);
36238 		jx9_result_bool(pCtx, 0);
36239 		return JX9_OK;
36240 	}
36241 	/* Perform the requested operation */
36242 	n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
36243 	/* IO result */
36244 	if( n < 1 ){
36245 		/* EOF or error, return FALSE */
36246 		jx9_result_bool(pCtx, 0);
36247 	}else{
36248 		/* Return the string holding the character */
36249 		jx9_result_string(pCtx, (const char *)&c, sizeof(char));
36250 	}
36251 	return JX9_OK;
36252 }
36253 /*
36254  * string fgets(resource $handle[, int64 $length ])
36255  *  Gets line from file pointer.
36256  * Parameters
36257  *  $handle
36258  *   The file pointer.
36259  * $length
36260  *  Reading ends when length - 1 bytes have been read, on a newline
36261  *  (which is included in the return value), or on EOF (whichever comes first).
36262  *  If no length is specified, it will keep reading from the stream until it reaches
36263  *  the end of the line.
36264  * Return
36265  *  Returns a string of up to length - 1 bytes read from the file pointed to by handle.
36266  *  If there is no more data to read in the file pointer, then FALSE is returned.
36267  *  If an error occurs, FALSE is returned.
36268  */
jx9Builtin_fgets(jx9_context * pCtx,int nArg,jx9_value ** apArg)36269 static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
36270 {
36271 	const jx9_io_stream *pStream;
36272 	const char *zLine;
36273 	io_private *pDev;
36274 	jx9_int64 n, nLen;
36275 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36276 		/* Missing/Invalid arguments, return FALSE */
36277 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36278 		jx9_result_bool(pCtx, 0);
36279 		return JX9_OK;
36280 	}
36281 	/* Extract our private data */
36282 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36283 	/* Make sure we are dealing with a valid io_private instance */
36284 	if( IO_PRIVATE_INVALID(pDev) ){
36285 		/*Expecting an IO handle */
36286 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36287 		jx9_result_bool(pCtx, 0);
36288 		return JX9_OK;
36289 	}
36290 	/* Point to the target IO stream device */
36291 	pStream = pDev->pStream;
36292 	if( pStream == 0  ){
36293 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36294 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36295 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36296 			);
36297 		jx9_result_bool(pCtx, 0);
36298 		return JX9_OK;
36299 	}
36300 	nLen = -1;
36301 	if( nArg > 1 ){
36302 		/* Maximum data to read */
36303 		nLen = jx9_value_to_int64(apArg[1]);
36304 	}
36305 	/* Perform the requested operation */
36306 	n = StreamReadLine(pDev, &zLine, nLen);
36307 	if( n < 1 ){
36308 		/* EOF or IO error, return FALSE */
36309 		jx9_result_bool(pCtx, 0);
36310 	}else{
36311 		/* Return the freshly extracted line */
36312 		jx9_result_string(pCtx, zLine, (int)n);
36313 	}
36314 	return JX9_OK;
36315 }
36316 /*
36317  * string fread(resource $handle, int64 $length)
36318  *  Binary-safe file read.
36319  * Parameters
36320  *  $handle
36321  *   The file pointer.
36322  * $length
36323  *  Up to length number of bytes read.
36324  * Return
36325  *  The data readen on success or FALSE on failure.
36326  */
jx9Builtin_fread(jx9_context * pCtx,int nArg,jx9_value ** apArg)36327 static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
36328 {
36329 	const jx9_io_stream *pStream;
36330 	io_private *pDev;
36331 	jx9_int64 nRead;
36332 	void *pBuf;
36333 	int nLen;
36334 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36335 		/* Missing/Invalid arguments, return FALSE */
36336 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36337 		jx9_result_bool(pCtx, 0);
36338 		return JX9_OK;
36339 	}
36340 	/* Extract our private data */
36341 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36342 	/* Make sure we are dealing with a valid io_private instance */
36343 	if( IO_PRIVATE_INVALID(pDev) ){
36344 		/*Expecting an IO handle */
36345 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36346 		jx9_result_bool(pCtx, 0);
36347 		return JX9_OK;
36348 	}
36349 	/* Point to the target IO stream device */
36350 	pStream = pDev->pStream;
36351 	if( pStream == 0  ){
36352 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36353 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36354 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36355 			);
36356 		jx9_result_bool(pCtx, 0);
36357 		return JX9_OK;
36358 	}
36359         nLen = 4096;
36360 	if( nArg > 1 ){
36361  	  nLen = jx9_value_to_int(apArg[1]);
36362 	  if( nLen < 1 ){
36363 		/* Invalid length, set a default length */
36364 		nLen = 4096;
36365 	  }
36366         }
36367 	/* Allocate enough buffer */
36368 	pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
36369 	if( pBuf == 0 ){
36370 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36371 		jx9_result_bool(pCtx, 0);
36372 		return JX9_OK;
36373 	}
36374 	/* Perform the requested operation */
36375 	nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
36376 	if( nRead < 1 ){
36377 		/* Nothing read, return FALSE */
36378 		jx9_result_bool(pCtx, 0);
36379 	}else{
36380 		/* Make a copy of the data just read */
36381 		jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
36382 	}
36383 	/* Release the buffer */
36384 	jx9_context_free_chunk(pCtx, pBuf);
36385 	return JX9_OK;
36386 }
36387 /*
36388  * array fgetcsv(resource $handle [, int $length = 0
36389  *         [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]])
36390  * Gets line from file pointer and parse for CSV fields.
36391  * Parameters
36392  * $handle
36393  *   The file pointer.
36394  * $length
36395  *  Reading ends when length - 1 bytes have been read, on a newline
36396  *  (which is included in the return value), or on EOF (whichever comes first).
36397  *  If no length is specified, it will keep reading from the stream until it reaches
36398  *  the end of the line.
36399  * $delimiter
36400  *   Set the field delimiter (one character only).
36401  * $enclosure
36402  *   Set the field enclosure character (one character only).
36403  * $escape
36404  *   Set the escape character (one character only). Defaults as a backslash (\)
36405  * Return
36406  *  Returns a string of up to length - 1 bytes read from the file pointed to by handle.
36407  *  If there is no more data to read in the file pointer, then FALSE is returned.
36408  *  If an error occurs, FALSE is returned.
36409  */
jx9Builtin_fgetcsv(jx9_context * pCtx,int nArg,jx9_value ** apArg)36410 static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
36411 {
36412 	const jx9_io_stream *pStream;
36413 	const char *zLine;
36414 	io_private *pDev;
36415 	jx9_int64 n, nLen;
36416 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36417 		/* Missing/Invalid arguments, return FALSE */
36418 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36419 		jx9_result_bool(pCtx, 0);
36420 		return JX9_OK;
36421 	}
36422 	/* Extract our private data */
36423 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36424 	/* Make sure we are dealing with a valid io_private instance */
36425 	if( IO_PRIVATE_INVALID(pDev) ){
36426 		/*Expecting an IO handle */
36427 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36428 		jx9_result_bool(pCtx, 0);
36429 		return JX9_OK;
36430 	}
36431 	/* Point to the target IO stream device */
36432 	pStream = pDev->pStream;
36433 	if( pStream == 0  ){
36434 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36435 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36436 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36437 			);
36438 		jx9_result_bool(pCtx, 0);
36439 		return JX9_OK;
36440 	}
36441 	nLen = -1;
36442 	if( nArg > 1 ){
36443 		/* Maximum data to read */
36444 		nLen = jx9_value_to_int64(apArg[1]);
36445 	}
36446 	/* Perform the requested operation */
36447 	n = StreamReadLine(pDev, &zLine, nLen);
36448 	if( n < 1 ){
36449 		/* EOF or IO error, return FALSE */
36450 		jx9_result_bool(pCtx, 0);
36451 	}else{
36452 		jx9_value *pArray;
36453 		int delim  = ',';   /* Delimiter */
36454 		int encl   = '"' ;  /* Enclosure */
36455 		int escape = '\\';  /* Escape character */
36456 		if( nArg > 2 ){
36457 			const char *zPtr;
36458 			int i;
36459 			if( jx9_value_is_string(apArg[2]) ){
36460 				/* Extract the delimiter */
36461 				zPtr = jx9_value_to_string(apArg[2], &i);
36462 				if( i > 0 ){
36463 					delim = zPtr[0];
36464 				}
36465 			}
36466 			if( nArg > 3 ){
36467 				if( jx9_value_is_string(apArg[3]) ){
36468 					/* Extract the enclosure */
36469 					zPtr = jx9_value_to_string(apArg[3], &i);
36470 					if( i > 0 ){
36471 						encl = zPtr[0];
36472 					}
36473 				}
36474 				if( nArg > 4 ){
36475 					if( jx9_value_is_string(apArg[4]) ){
36476 						/* Extract the escape character */
36477 						zPtr = jx9_value_to_string(apArg[4], &i);
36478 						if( i > 0 ){
36479 							escape = zPtr[0];
36480 						}
36481 					}
36482 				}
36483 			}
36484 		}
36485 		/* Create our array */
36486 		pArray = jx9_context_new_array(pCtx);
36487 		if( pArray == 0 ){
36488 			jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36489 			jx9_result_null(pCtx);
36490 			return JX9_OK;
36491 		}
36492 		/* Parse the raw input */
36493 		jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
36494 		/* Return the freshly created array  */
36495 		jx9_result_value(pCtx, pArray);
36496 	}
36497 	return JX9_OK;
36498 }
36499 /*
36500  * string fgetss(resource $handle [, int $length [, string $allowable_tags ]])
36501  *  Gets line from file pointer and strip HTML tags.
36502  * Parameters
36503  * $handle
36504  *   The file pointer.
36505  * $length
36506  *  Reading ends when length - 1 bytes have been read, on a newline
36507  *  (which is included in the return value), or on EOF (whichever comes first).
36508  *  If no length is specified, it will keep reading from the stream until it reaches
36509  *  the end of the line.
36510  * $allowable_tags
36511  *  You can use the optional second parameter to specify tags which should not be stripped.
36512  * Return
36513  *  Returns a string of up to length - 1 bytes read from the file pointed to by
36514  *  handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE.
36515  */
jx9Builtin_fgetss(jx9_context * pCtx,int nArg,jx9_value ** apArg)36516 static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
36517 {
36518 	const jx9_io_stream *pStream;
36519 	const char *zLine;
36520 	io_private *pDev;
36521 	jx9_int64 n, nLen;
36522 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36523 		/* Missing/Invalid arguments, return FALSE */
36524 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36525 		jx9_result_bool(pCtx, 0);
36526 		return JX9_OK;
36527 	}
36528 	/* Extract our private data */
36529 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36530 	/* Make sure we are dealing with a valid io_private instance */
36531 	if( IO_PRIVATE_INVALID(pDev) ){
36532 		/*Expecting an IO handle */
36533 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36534 		jx9_result_bool(pCtx, 0);
36535 		return JX9_OK;
36536 	}
36537 	/* Point to the target IO stream device */
36538 	pStream = pDev->pStream;
36539 	if( pStream == 0  ){
36540 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36541 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36542 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36543 			);
36544 		jx9_result_bool(pCtx, 0);
36545 		return JX9_OK;
36546 	}
36547 	nLen = -1;
36548 	if( nArg > 1 ){
36549 		/* Maximum data to read */
36550 		nLen = jx9_value_to_int64(apArg[1]);
36551 	}
36552 	/* Perform the requested operation */
36553 	n = StreamReadLine(pDev, &zLine, nLen);
36554 	if( n < 1 ){
36555 		/* EOF or IO error, return FALSE */
36556 		jx9_result_bool(pCtx, 0);
36557 	}else{
36558 		const char *zTaglist = 0;
36559 		int nTaglen = 0;
36560 		if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
36561 			/* Allowed tag */
36562 			zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
36563 		}
36564 		/* Process data just read */
36565 		jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
36566 	}
36567 	return JX9_OK;
36568 }
36569 /*
36570  * string readdir(resource $dir_handle)
36571  *   Read entry from directory handle.
36572  * Parameter
36573  *  $dir_handle
36574  *   The directory handle resource previously opened with opendir().
36575  * Return
36576  *  Returns the filename on success or FALSE on failure.
36577  */
jx9Builtin_readdir(jx9_context * pCtx,int nArg,jx9_value ** apArg)36578 static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36579 {
36580 	const jx9_io_stream *pStream;
36581 	io_private *pDev;
36582 	int rc;
36583 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36584 		/* Missing/Invalid arguments, return FALSE */
36585 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36586 		jx9_result_bool(pCtx, 0);
36587 		return JX9_OK;
36588 	}
36589 	/* Extract our private data */
36590 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36591 	/* Make sure we are dealing with a valid io_private instance */
36592 	if( IO_PRIVATE_INVALID(pDev) ){
36593 		/*Expecting an IO handle */
36594 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36595 		jx9_result_bool(pCtx, 0);
36596 		return JX9_OK;
36597 	}
36598 	/* Point to the target IO stream device */
36599 	pStream = pDev->pStream;
36600 	if( pStream == 0  || pStream->xReadDir == 0 ){
36601 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36602 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36603 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36604 			);
36605 		jx9_result_bool(pCtx, 0);
36606 		return JX9_OK;
36607 	}
36608 	jx9_result_bool(pCtx, 0);
36609 	/* Perform the requested operation */
36610 	rc = pStream->xReadDir(pDev->pHandle, pCtx);
36611 	if( rc != JX9_OK ){
36612 		/* Return FALSE */
36613 		jx9_result_bool(pCtx, 0);
36614 	}
36615 	return JX9_OK;
36616 }
36617 /*
36618  * void rewinddir(resource $dir_handle)
36619  *   Rewind directory handle.
36620  * Parameter
36621  *  $dir_handle
36622  *   The directory handle resource previously opened with opendir().
36623  * Return
36624  *  FALSE on failure.
36625  */
jx9Builtin_rewinddir(jx9_context * pCtx,int nArg,jx9_value ** apArg)36626 static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36627 {
36628 	const jx9_io_stream *pStream;
36629 	io_private *pDev;
36630 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36631 		/* Missing/Invalid arguments, return FALSE */
36632 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36633 		jx9_result_bool(pCtx, 0);
36634 		return JX9_OK;
36635 	}
36636 	/* Extract our private data */
36637 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36638 	/* Make sure we are dealing with a valid io_private instance */
36639 	if( IO_PRIVATE_INVALID(pDev) ){
36640 		/*Expecting an IO handle */
36641 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36642 		jx9_result_bool(pCtx, 0);
36643 		return JX9_OK;
36644 	}
36645 	/* Point to the target IO stream device */
36646 	pStream = pDev->pStream;
36647 	if( pStream == 0  || pStream->xRewindDir == 0 ){
36648 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36649 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36650 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36651 			);
36652 		jx9_result_bool(pCtx, 0);
36653 		return JX9_OK;
36654 	}
36655 	/* Perform the requested operation */
36656 	pStream->xRewindDir(pDev->pHandle);
36657 	return JX9_OK;
36658  }
36659 /* Forward declaration */
36660 static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
36661 static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev);
36662 /*
36663  * void closedir(resource $dir_handle)
36664  *   Close directory handle.
36665  * Parameter
36666  *  $dir_handle
36667  *   The directory handle resource previously opened with opendir().
36668  * Return
36669  *  FALSE on failure.
36670  */
jx9Builtin_closedir(jx9_context * pCtx,int nArg,jx9_value ** apArg)36671 static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36672 {
36673 	const jx9_io_stream *pStream;
36674 	io_private *pDev;
36675 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
36676 		/* Missing/Invalid arguments, return FALSE */
36677 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36678 		jx9_result_bool(pCtx, 0);
36679 		return JX9_OK;
36680 	}
36681 	/* Extract our private data */
36682 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
36683 	/* Make sure we are dealing with a valid io_private instance */
36684 	if( IO_PRIVATE_INVALID(pDev) ){
36685 		/*Expecting an IO handle */
36686 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
36687 		jx9_result_bool(pCtx, 0);
36688 		return JX9_OK;
36689 	}
36690 	/* Point to the target IO stream device */
36691 	pStream = pDev->pStream;
36692 	if( pStream == 0  || pStream->xCloseDir == 0 ){
36693 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36694 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
36695 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
36696 			);
36697 		jx9_result_bool(pCtx, 0);
36698 		return JX9_OK;
36699 	}
36700 	/* Perform the requested operation */
36701 	pStream->xCloseDir(pDev->pHandle);
36702 	/* Release the private stucture */
36703 	ReleaseIOPrivate(pCtx, pDev);
36704 	jx9MemObjRelease(apArg[0]);
36705 	return JX9_OK;
36706  }
36707 /*
36708  * resource opendir(string $path[, resource $context])
36709  *  Open directory handle.
36710  * Parameters
36711  * $path
36712  *   The directory path that is to be opened.
36713  * $context
36714  *   A context stream resource.
36715  * Return
36716  *  A directory handle resource on success, or FALSE on failure.
36717  */
jx9Builtin_opendir(jx9_context * pCtx,int nArg,jx9_value ** apArg)36718 static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
36719 {
36720 	const jx9_io_stream *pStream;
36721 	const char *zPath;
36722 	io_private *pDev;
36723 	int iLen, rc;
36724 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36725 		/* Missing/Invalid arguments, return FALSE */
36726 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
36727 		jx9_result_bool(pCtx, 0);
36728 		return JX9_OK;
36729 	}
36730 	/* Extract the target path */
36731 	zPath  = jx9_value_to_string(apArg[0], &iLen);
36732 	/* Try to extract a stream */
36733 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
36734 	if( pStream == 0 ){
36735 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36736 			"No stream device is associated with the given path(%s)", zPath);
36737 		jx9_result_bool(pCtx, 0);
36738 		return JX9_OK;
36739 	}
36740 	if( pStream->xOpenDir == 0 ){
36741 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
36742 			"IO routine(%s) not implemented in the underlying stream(%s) device",
36743 			jx9_function_name(pCtx), pStream->zName
36744 			);
36745 		jx9_result_bool(pCtx, 0);
36746 		return JX9_OK;
36747 	}
36748 	/* Allocate a new IO private instance */
36749 	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
36750 	if( pDev == 0 ){
36751 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
36752 		jx9_result_bool(pCtx, 0);
36753 		return JX9_OK;
36754 	}
36755 	/* Initialize the structure */
36756 	InitIOPrivate(pCtx->pVm, pStream, pDev);
36757 	/* Open the target directory */
36758 	rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle);
36759 	if( rc != JX9_OK ){
36760 		/* IO error, return FALSE */
36761 		ReleaseIOPrivate(pCtx, pDev);
36762 		jx9_result_bool(pCtx, 0);
36763 	}else{
36764 		/* Return the handle as a resource */
36765 		jx9_result_resource(pCtx, pDev);
36766 	}
36767 	return JX9_OK;
36768 }
36769 /*
36770  * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]])
36771  *  Reads a file and writes it to the output buffer.
36772  * Parameters
36773  *  $filename
36774  *   The filename being read.
36775  *  $use_include_path
36776  *   You can use the optional second parameter and set it to
36777  *   TRUE, if you want to search for the file in the include_path, too.
36778  *  $context
36779  *   A context stream resource.
36780  * Return
36781  *  The number of bytes read from the file on success or FALSE on failure.
36782  */
jx9Builtin_readfile(jx9_context * pCtx,int nArg,jx9_value ** apArg)36783 static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
36784 {
36785 	int use_include  = FALSE;
36786 	const jx9_io_stream *pStream;
36787 	jx9_int64 n, nRead;
36788 	const char *zFile;
36789 	char zBuf[8192];
36790 	void *pHandle;
36791 	int rc, nLen;
36792 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36793 		/* Missing/Invalid arguments, return FALSE */
36794 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
36795 		jx9_result_bool(pCtx, 0);
36796 		return JX9_OK;
36797 	}
36798 	/* Extract the file path */
36799 	zFile = jx9_value_to_string(apArg[0], &nLen);
36800 	/* Point to the target IO stream device */
36801 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
36802 	if( pStream == 0 ){
36803 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
36804 		jx9_result_bool(pCtx, 0);
36805 		return JX9_OK;
36806 	}
36807 	if( nArg > 1 ){
36808 		use_include = jx9_value_to_bool(apArg[1]);
36809 	}
36810 	/* Try to open the file in read-only mode */
36811 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY,
36812 		use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
36813 	if( pHandle == 0 ){
36814 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
36815 		jx9_result_bool(pCtx, 0);
36816 		return JX9_OK;
36817 	}
36818 	/* Perform the requested operation */
36819 	nRead = 0;
36820 	for(;;){
36821 		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
36822 		if( n < 1 ){
36823 			/* EOF or IO error, break immediately */
36824 			break;
36825 		}
36826 		/* Output data */
36827 		rc = jx9_context_output(pCtx, zBuf, (int)n);
36828 		if( rc == JX9_ABORT ){
36829 			break;
36830 		}
36831 		/* Increment counter */
36832 		nRead += n;
36833 	}
36834 	/* Close the stream */
36835 	jx9StreamCloseHandle(pStream, pHandle);
36836 	/* Total number of bytes readen */
36837 	jx9_result_int64(pCtx, nRead);
36838 	return JX9_OK;
36839 }
36840 /*
36841  * string file_get_contents(string $filename[, bool $use_include_path = false
36842  *         [, resource $context [, int $offset = -1 [, int $maxlen ]]]])
36843  *  Reads entire file into a string.
36844  * Parameters
36845  *  $filename
36846  *   The filename being read.
36847  *  $use_include_path
36848  *   You can use the optional second parameter and set it to
36849  *   TRUE, if you want to search for the file in the include_path, too.
36850  *  $context
36851  *   A context stream resource.
36852  *  $offset
36853  *   The offset where the reading starts on the original stream.
36854  *  $maxlen
36855  *    Maximum length of data read. The default is to read until end of file
36856  *    is reached. Note that this parameter is applied to the stream processed by the filters.
36857  * Return
36858  *   The function returns the read data or FALSE on failure.
36859  */
jx9Builtin_file_get_contents(jx9_context * pCtx,int nArg,jx9_value ** apArg)36860 static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
36861 {
36862 	const jx9_io_stream *pStream;
36863 	jx9_int64 n, nRead, nMaxlen;
36864 	int use_include  = FALSE;
36865 	const char *zFile;
36866 	char zBuf[8192];
36867 	void *pHandle;
36868 	int nLen;
36869 
36870 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
36871 		/* Missing/Invalid arguments, return FALSE */
36872 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
36873 		jx9_result_bool(pCtx, 0);
36874 		return JX9_OK;
36875 	}
36876 	/* Extract the file path */
36877 	zFile = jx9_value_to_string(apArg[0], &nLen);
36878 	/* Point to the target IO stream device */
36879 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
36880 	if( pStream == 0 ){
36881 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
36882 		jx9_result_bool(pCtx, 0);
36883 		return JX9_OK;
36884 	}
36885 	nMaxlen = -1;
36886 	if( nArg > 1 ){
36887 		use_include = jx9_value_to_bool(apArg[1]);
36888 	}
36889 	/* Try to open the file in read-only mode */
36890 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
36891 	if( pHandle == 0 ){
36892 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
36893 		jx9_result_bool(pCtx, 0);
36894 		return JX9_OK;
36895 	}
36896 	if( nArg > 3 ){
36897 		/* Extract the offset */
36898 		n = jx9_value_to_int64(apArg[3]);
36899 		if( n > 0 ){
36900 			if( pStream->xSeek ){
36901 				/* Seek to the desired offset */
36902 				pStream->xSeek(pHandle, n, 0/*SEEK_SET*/);
36903 			}
36904 		}
36905 		if( nArg > 4 ){
36906 			/* Maximum data to read */
36907 			nMaxlen = jx9_value_to_int64(apArg[4]);
36908 		}
36909 	}
36910 	/* Perform the requested operation */
36911 	nRead = 0;
36912 	for(;;){
36913 		n = pStream->xRead(pHandle, zBuf,
36914 			(nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf));
36915 		if( n < 1 ){
36916 			/* EOF or IO error, break immediately */
36917 			break;
36918 		}
36919 		/* Append data */
36920 		jx9_result_string(pCtx, zBuf, (int)n);
36921 		/* Increment read counter */
36922 		nRead += n;
36923 		if( nMaxlen > 0 && nRead >= nMaxlen ){
36924 			/* Read limit reached */
36925 			break;
36926 		}
36927 	}
36928 	/* Close the stream */
36929 	jx9StreamCloseHandle(pStream, pHandle);
36930 	/* Check if we have read something */
36931 	if( jx9_context_result_buf_length(pCtx) < 1 ){
36932 		/* Nothing read, return FALSE */
36933 		jx9_result_bool(pCtx, 0);
36934 	}
36935 	return JX9_OK;
36936 }
36937 /*
36938  * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]])
36939  *  Write a string to a file.
36940  * Parameters
36941  *  $filename
36942  *  Path to the file where to write the data.
36943  * $data
36944  *  The data to write(Must be a string).
36945  * $flags
36946  *  The value of flags can be any combination of the following
36947  * flags, joined with the binary OR (|) operator.
36948  *   FILE_USE_INCLUDE_PATH 	Search for filename in the include directory. See include_path for more information.
36949  *   FILE_APPEND 	        If file filename already exists, append the data to the file instead of overwriting it.
36950  *   LOCK_EX 	            Acquire an exclusive lock on the file while proceeding to the writing.
36951  * context
36952  *  A context stream resource.
36953  * Return
36954  *  The function returns the number of bytes that were written to the file, or FALSE on failure.
36955  */
jx9Builtin_file_put_contents(jx9_context * pCtx,int nArg,jx9_value ** apArg)36956 static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
36957 {
36958 	int use_include  = FALSE;
36959 	const jx9_io_stream *pStream;
36960 	const char *zFile;
36961 	const char *zData;
36962 	int iOpenFlags;
36963 	void *pHandle;
36964 	int iFlags;
36965 	int nLen;
36966 
36967 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
36968 		/* Missing/Invalid arguments, return FALSE */
36969 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
36970 		jx9_result_bool(pCtx, 0);
36971 		return JX9_OK;
36972 	}
36973 	/* Extract the file path */
36974 	zFile = jx9_value_to_string(apArg[0], &nLen);
36975 	/* Point to the target IO stream device */
36976 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
36977 	if( pStream == 0 ){
36978 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
36979 		jx9_result_bool(pCtx, 0);
36980 		return JX9_OK;
36981 	}
36982 	/* Data to write */
36983 	zData = jx9_value_to_string(apArg[1], &nLen);
36984 	if( nLen < 1 ){
36985 		/* Nothing to write, return immediately */
36986 		jx9_result_bool(pCtx, 0);
36987 		return JX9_OK;
36988 	}
36989 	/* Try to open the file in read-write mode */
36990 	iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
36991 	/* Extract the flags */
36992 	iFlags = 0;
36993 	if( nArg > 2 ){
36994 		iFlags = jx9_value_to_int(apArg[2]);
36995 		if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){
36996 			use_include = TRUE;
36997 		}
36998 		if( iFlags & 0x08 /* FILE_APPEND */){
36999 			/* If the file already exists, append the data to the file
37000 			 * instead of overwriting it.
37001 			 */
37002 			iOpenFlags &= ~JX9_IO_OPEN_TRUNC;
37003 			/* Append mode */
37004 			iOpenFlags |= JX9_IO_OPEN_APPEND;
37005 		}
37006 	}
37007 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include,
37008 		nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
37009 	if( pHandle == 0 ){
37010 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
37011 		jx9_result_bool(pCtx, 0);
37012 		return JX9_OK;
37013 	}
37014 	if( pStream->xWrite ){
37015 		jx9_int64 n;
37016 		if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){
37017 			/* Try to acquire an exclusive lock */
37018 			pStream->xLock(pHandle, 1/* LOCK_EX */);
37019 		}
37020 		/* Perform the write operation */
37021 		n = pStream->xWrite(pHandle, (const void *)zData, nLen);
37022 		if( n < 1 ){
37023 			/* IO error, return FALSE */
37024 			jx9_result_bool(pCtx, 0);
37025 		}else{
37026 			/* Total number of bytes written */
37027 			jx9_result_int64(pCtx, n);
37028 		}
37029 	}else{
37030 		/* Read-only stream */
37031 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR,
37032 			"Read-only stream(%s): Cannot perform write operation",
37033 			pStream ? pStream->zName : "null_stream"
37034 			);
37035 		jx9_result_bool(pCtx, 0);
37036 	}
37037 	/* Close the handle */
37038 	jx9StreamCloseHandle(pStream, pHandle);
37039 	return JX9_OK;
37040 }
37041 /*
37042  * array file(string $filename[, int $flags = 0[, resource $context]])
37043  *  Reads entire file into an array.
37044  * Parameters
37045  *  $filename
37046  *   The filename being read.
37047  *  $flags
37048  *   The optional parameter flags can be one, or more, of the following constants:
37049  *   FILE_USE_INCLUDE_PATH
37050  *       Search for the file in the include_path.
37051  *   FILE_IGNORE_NEW_LINES
37052  *       Do not add newline at the end of each array element
37053  *   FILE_SKIP_EMPTY_LINES
37054  *       Skip empty lines
37055  *  $context
37056  *   A context stream resource.
37057  * Return
37058  *   The function returns the read data or FALSE on failure.
37059  */
jx9Builtin_file(jx9_context * pCtx,int nArg,jx9_value ** apArg)37060 static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
37061 {
37062 	const char *zFile, *zPtr, *zEnd, *zBuf;
37063 	jx9_value *pArray, *pLine;
37064 	const jx9_io_stream *pStream;
37065 	int use_include = 0;
37066 	io_private *pDev;
37067 	jx9_int64 n;
37068 	int iFlags;
37069 	int nLen;
37070 
37071 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
37072 		/* Missing/Invalid arguments, return FALSE */
37073 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
37074 		jx9_result_bool(pCtx, 0);
37075 		return JX9_OK;
37076 	}
37077 	/* Extract the file path */
37078 	zFile = jx9_value_to_string(apArg[0], &nLen);
37079 	/* Point to the target IO stream device */
37080 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37081 	if( pStream == 0 ){
37082 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37083 		jx9_result_bool(pCtx, 0);
37084 		return JX9_OK;
37085 	}
37086 	/* Allocate a new IO private instance */
37087 	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
37088 	if( pDev == 0 ){
37089 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37090 		jx9_result_bool(pCtx, 0);
37091 		return JX9_OK;
37092 	}
37093 	/* Initialize the structure */
37094 	InitIOPrivate(pCtx->pVm, pStream, pDev);
37095 	iFlags = 0;
37096 	if( nArg > 1 ){
37097 		iFlags = jx9_value_to_int(apArg[1]);
37098 	}
37099 	if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
37100 		use_include = TRUE;
37101 	}
37102 	/* Create the array and the working value */
37103 	pArray = jx9_context_new_array(pCtx);
37104 	pLine = jx9_context_new_scalar(pCtx);
37105 	if( pArray == 0 || pLine == 0 ){
37106 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37107 		jx9_result_bool(pCtx, 0);
37108 		return JX9_OK;
37109 	}
37110 	/* Try to open the file in read-only mode */
37111 	pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37112 	if( pDev->pHandle == 0 ){
37113 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
37114 		jx9_result_bool(pCtx, 0);
37115 		/* Don't worry about freeing memory, everything will be released automatically
37116 		 * as soon we return from this function.
37117 		 */
37118 		return JX9_OK;
37119 	}
37120 	/* Perform the requested operation */
37121 	for(;;){
37122 		/* Try to extract a line */
37123 		n = StreamReadLine(pDev, &zBuf, -1);
37124 		if( n < 1 ){
37125 			/* EOF or IO error */
37126 			break;
37127 		}
37128 		/* Reset the cursor */
37129 		jx9_value_reset_string_cursor(pLine);
37130 		/* Remove line ending if requested by the caller */
37131 		zPtr = zBuf;
37132 		zEnd = &zBuf[n];
37133 		if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){
37134 			/* Ignore trailig lines */
37135 			while( zPtr < zEnd && (zEnd[-1] == '\n'
37136 #ifdef __WINNT__
37137 				|| zEnd[-1] == '\r'
37138 #endif
37139 				)){
37140 					n--;
37141 					zEnd--;
37142 			}
37143 		}
37144 		if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){
37145 			/* Ignore empty lines */
37146 			while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){
37147 				zPtr++;
37148 			}
37149 			if( zPtr >= zEnd ){
37150 				/* Empty line */
37151 				continue;
37152 			}
37153 		}
37154 		jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
37155 		/* Insert line */
37156 		jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
37157 	}
37158 	/* Close the stream */
37159 	jx9StreamCloseHandle(pStream, pDev->pHandle);
37160 	/* Release the io_private instance */
37161 	ReleaseIOPrivate(pCtx, pDev);
37162 	/* Return the created array */
37163 	jx9_result_value(pCtx, pArray);
37164 	return JX9_OK;
37165 }
37166 /*
37167  * bool copy(string $source, string $dest[, resource $context ] )
37168  *  Makes a copy of the file source to dest.
37169  * Parameters
37170  *  $source
37171  *   Path to the source file.
37172  *  $dest
37173  *   The destination path. If dest is a URL, the copy operation
37174  *   may fail if the wrapper does not support overwriting of existing files.
37175  *  $context
37176  *   A context stream resource.
37177  * Return
37178  *  TRUE on success or FALSE on failure.
37179  */
jx9Builtin_copy(jx9_context * pCtx,int nArg,jx9_value ** apArg)37180 static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
37181 {
37182 	const jx9_io_stream *pSin, *pSout;
37183 	const char *zFile;
37184 	char zBuf[8192];
37185 	void *pIn, *pOut;
37186 	jx9_int64 n;
37187 	int nLen;
37188 	if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
37189 		/* Missing/Invalid arguments, return FALSE */
37190 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
37191 		jx9_result_bool(pCtx, 0);
37192 		return JX9_OK;
37193 	}
37194 	/* Extract the source name */
37195 	zFile = jx9_value_to_string(apArg[0], &nLen);
37196 	/* Point to the target IO stream device */
37197 	pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37198 	if( pSin == 0 ){
37199 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37200 		jx9_result_bool(pCtx, 0);
37201 		return JX9_OK;
37202 	}
37203 	/* Try to open the source file in a read-only mode */
37204 	pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37205 	if( pIn == 0 ){
37206 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
37207 		jx9_result_bool(pCtx, 0);
37208 		return JX9_OK;
37209 	}
37210 	/* Extract the destination name */
37211 	zFile = jx9_value_to_string(apArg[1], &nLen);
37212 	/* Point to the target IO stream device */
37213 	pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
37214 	if( pSout == 0 ){
37215 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
37216 		jx9_result_bool(pCtx, 0);
37217 		jx9StreamCloseHandle(pSin, pIn);
37218 		return JX9_OK;
37219 	}
37220 	if( pSout->xWrite == 0 ){
37221 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37222 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37223 			jx9_function_name(pCtx), pSin->zName
37224 			);
37225 		jx9_result_bool(pCtx, 0);
37226 		jx9StreamCloseHandle(pSin, pIn);
37227 		return JX9_OK;
37228 	}
37229 	/* Try to open the destination file in a read-write mode */
37230 	pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile,
37231 		JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
37232 	if( pOut == 0 ){
37233 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
37234 		jx9_result_bool(pCtx, 0);
37235 		jx9StreamCloseHandle(pSin, pIn);
37236 		return JX9_OK;
37237 	}
37238 	/* Perform the requested operation */
37239 	for(;;){
37240 		/* Read from source */
37241 		n = pSin->xRead(pIn, zBuf, sizeof(zBuf));
37242 		if( n < 1 ){
37243 			/* EOF or IO error, break immediately */
37244 			break;
37245 		}
37246 		/* Write to dest */
37247 		n = pSout->xWrite(pOut, zBuf, n);
37248 		if( n < 1 ){
37249 			/* IO error, break immediately */
37250 			break;
37251 		}
37252 	}
37253 	/* Close the streams */
37254 	jx9StreamCloseHandle(pSin, pIn);
37255 	jx9StreamCloseHandle(pSout, pOut);
37256 	/* Return TRUE */
37257 	jx9_result_bool(pCtx, 1);
37258 	return JX9_OK;
37259 }
37260 /*
37261  * array fstat(resource $handle)
37262  *  Gets information about a file using an open file pointer.
37263  * Parameters
37264  *  $handle
37265  *   The file pointer.
37266  * Return
37267  *  Returns an array with the statistics of the file or FALSE on failure.
37268  */
jx9Builtin_fstat(jx9_context * pCtx,int nArg,jx9_value ** apArg)37269 static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
37270 {
37271 	jx9_value *pArray, *pValue;
37272 	const jx9_io_stream *pStream;
37273 	io_private *pDev;
37274 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
37275 		/* Missing/Invalid arguments, return FALSE */
37276 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37277 		jx9_result_bool(pCtx, 0);
37278 		return JX9_OK;
37279 	}
37280 	/* Extract our private data */
37281 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37282 	/* Make sure we are dealing with a valid io_private instance */
37283 	if( IO_PRIVATE_INVALID(pDev) ){
37284 		/* Expecting an IO handle */
37285 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37286 		jx9_result_bool(pCtx, 0);
37287 		return JX9_OK;
37288 	}
37289 	/* Point to the target IO stream device */
37290 	pStream = pDev->pStream;
37291 	if( pStream == 0  || pStream->xStat == 0){
37292 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37293 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37294 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37295 			);
37296 		jx9_result_bool(pCtx, 0);
37297 		return JX9_OK;
37298 	}
37299 	/* Create the array and the working value */
37300 	pArray = jx9_context_new_array(pCtx);
37301 	pValue = jx9_context_new_scalar(pCtx);
37302 	if( pArray == 0 || pValue == 0 ){
37303 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37304 		jx9_result_bool(pCtx, 0);
37305 		return JX9_OK;
37306 	}
37307 	/* Perform the requested operation */
37308 	pStream->xStat(pDev->pHandle, pArray, pValue);
37309 	/* Return the freshly created array */
37310 	jx9_result_value(pCtx, pArray);
37311 	/* Don't worry about freeing memory here, everything will be
37312 	 * released automatically as soon we return from this function.
37313 	 */
37314 	return JX9_OK;
37315 }
37316 /*
37317  * int fwrite(resource $handle, string $string[, int $length])
37318  *  Writes the contents of string to the file stream pointed to by handle.
37319  * Parameters
37320  *  $handle
37321  *   The file pointer.
37322  *  $string
37323  *   The string that is to be written.
37324  *  $length
37325  *   If the length argument is given, writing will stop after length bytes have been written
37326  *   or the end of string is reached, whichever comes first.
37327  * Return
37328  *  Returns the number of bytes written, or FALSE on error.
37329  */
jx9Builtin_fwrite(jx9_context * pCtx,int nArg,jx9_value ** apArg)37330 static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
37331 {
37332 	const jx9_io_stream *pStream;
37333 	const char *zString;
37334 	io_private *pDev;
37335 	int nLen, n;
37336 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
37337 		/* Missing/Invalid arguments, return FALSE */
37338 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37339 		jx9_result_bool(pCtx, 0);
37340 		return JX9_OK;
37341 	}
37342 	/* Extract our private data */
37343 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37344 	/* Make sure we are dealing with a valid io_private instance */
37345 	if( IO_PRIVATE_INVALID(pDev) ){
37346 		/* Expecting an IO handle */
37347 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37348 		jx9_result_bool(pCtx, 0);
37349 		return JX9_OK;
37350 	}
37351 	/* Point to the target IO stream device */
37352 	pStream = pDev->pStream;
37353 	if( pStream == 0  || pStream->xWrite == 0){
37354 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37355 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37356 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37357 			);
37358 		jx9_result_bool(pCtx, 0);
37359 		return JX9_OK;
37360 	}
37361 	/* Extract the data to write */
37362 	zString = jx9_value_to_string(apArg[1], &nLen);
37363 	if( nArg > 2 ){
37364 		/* Maximum data length to write */
37365 		n = jx9_value_to_int(apArg[2]);
37366 		if( n >= 0 && n < nLen ){
37367 			nLen = n;
37368 		}
37369 	}
37370 	if( nLen < 1 ){
37371 		/* Nothing to write */
37372 		jx9_result_int(pCtx, 0);
37373 		return JX9_OK;
37374 	}
37375 	/* Perform the requested operation */
37376 	n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
37377 	if( n <  0 ){
37378 		/* IO error, return FALSE */
37379 		jx9_result_bool(pCtx, 0);
37380 	}else{
37381 		/* #Bytes written */
37382 		jx9_result_int(pCtx, n);
37383 	}
37384 	return JX9_OK;
37385 }
37386 /*
37387  * bool flock(resource $handle, int $operation)
37388  *  Portable advisory file locking.
37389  * Parameters
37390  *  $handle
37391  *   The file pointer.
37392  *  $operation
37393  *   operation is one of the following:
37394  *      LOCK_SH to acquire a shared lock (reader).
37395  *      LOCK_EX to acquire an exclusive lock (writer).
37396  *      LOCK_UN to release a lock (shared or exclusive).
37397  * Return
37398  *  Returns TRUE on success or FALSE on failure.
37399  */
jx9Builtin_flock(jx9_context * pCtx,int nArg,jx9_value ** apArg)37400 static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
37401 {
37402 	const jx9_io_stream *pStream;
37403 	io_private *pDev;
37404 	int nLock;
37405 	int rc;
37406 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
37407 		/* Missing/Invalid arguments, return FALSE */
37408 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37409 		jx9_result_bool(pCtx, 0);
37410 		return JX9_OK;
37411 	}
37412 	/* Extract our private data */
37413 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37414 	/* Make sure we are dealing with a valid io_private instance */
37415 	if( IO_PRIVATE_INVALID(pDev) ){
37416 		/*Expecting an IO handle */
37417 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37418 		jx9_result_bool(pCtx, 0);
37419 		return JX9_OK;
37420 	}
37421 	/* Point to the target IO stream device */
37422 	pStream = pDev->pStream;
37423 	if( pStream == 0  || pStream->xLock == 0){
37424 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37425 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37426 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37427 			);
37428 		jx9_result_bool(pCtx, 0);
37429 		return JX9_OK;
37430 	}
37431 	/* Requested lock operation */
37432 	nLock = jx9_value_to_int(apArg[1]);
37433 	/* Lock operation */
37434 	rc = pStream->xLock(pDev->pHandle, nLock);
37435 	/* IO result */
37436 	jx9_result_bool(pCtx, rc == JX9_OK);
37437 	return JX9_OK;
37438 }
37439 /*
37440  * int fpassthru(resource $handle)
37441  *  Output all remaining data on a file pointer.
37442  * Parameters
37443  *  $handle
37444  *   The file pointer.
37445  * Return
37446  *  Total number of characters read from handle and passed through
37447  *  to the output on success or FALSE on failure.
37448  */
jx9Builtin_fpassthru(jx9_context * pCtx,int nArg,jx9_value ** apArg)37449 static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
37450 {
37451 	const jx9_io_stream *pStream;
37452 	io_private *pDev;
37453 	jx9_int64 n, nRead;
37454 	char zBuf[8192];
37455 	int rc;
37456 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
37457 		/* Missing/Invalid arguments, return FALSE */
37458 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37459 		jx9_result_bool(pCtx, 0);
37460 		return JX9_OK;
37461 	}
37462 	/* Extract our private data */
37463 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37464 	/* Make sure we are dealing with a valid io_private instance */
37465 	if( IO_PRIVATE_INVALID(pDev) ){
37466 		/*Expecting an IO handle */
37467 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37468 		jx9_result_bool(pCtx, 0);
37469 		return JX9_OK;
37470 	}
37471 	/* Point to the target IO stream device */
37472 	pStream = pDev->pStream;
37473 	if( pStream == 0  ){
37474 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37475 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37476 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37477 			);
37478 		jx9_result_bool(pCtx, 0);
37479 		return JX9_OK;
37480 	}
37481 	/* Perform the requested operation */
37482 	nRead = 0;
37483 	for(;;){
37484 		n = StreamRead(pDev, zBuf, sizeof(zBuf));
37485 		if( n < 1 ){
37486 			/* Error or EOF */
37487 			break;
37488 		}
37489 		/* Increment the read counter */
37490 		nRead += n;
37491 		/* Output data */
37492 		rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
37493 		if( rc == JX9_ABORT ){
37494 			/* Consumer callback request an operation abort */
37495 			break;
37496 		}
37497 	}
37498 	/* Total number of bytes readen */
37499 	jx9_result_int64(pCtx, nRead);
37500 	return JX9_OK;
37501 }
37502 /* CSV reader/writer private data */
37503 struct csv_data
37504 {
37505 	int delimiter;    /* Delimiter. Default ', ' */
37506 	int enclosure;    /* Enclosure. Default '"'*/
37507 	io_private *pDev; /* Open stream handle */
37508 	int iCount;       /* Counter */
37509 };
37510 /*
37511  * The following callback is used by the fputcsv() function inorder to iterate
37512  * throw array entries and output CSV data based on the current key and it's
37513  * associated data.
37514  */
csv_write_callback(jx9_value * pKey,jx9_value * pValue,void * pUserData)37515 static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
37516 {
37517 	struct csv_data *pData = (struct csv_data *)pUserData;
37518 	const char *zData;
37519 	int nLen, c2;
37520 	sxu32 n;
37521 	/* Point to the raw data */
37522 	zData = jx9_value_to_string(pValue, &nLen);
37523 	if( nLen < 1 ){
37524 		/* Nothing to write */
37525 		return JX9_OK;
37526 	}
37527 	if( pData->iCount > 0 ){
37528 		/* Write the delimiter */
37529 		pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char));
37530 	}
37531 	n = 1;
37532 	c2 = 0;
37533 	if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK ||
37534 		SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){
37535 			c2 = 1;
37536 			if( n == 0 ){
37537 				c2 = 2;
37538 			}
37539 			/* Write the enclosure */
37540 			pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37541 			if( c2 > 1 ){
37542 				pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37543 			}
37544 	}
37545 	/* Write the data */
37546 	if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){
37547 		SXUNUSED(pKey); /* cc warning */
37548 		return JX9_ABORT;
37549 	}
37550 	if( c2 > 0 ){
37551 		/* Write the enclosure */
37552 		pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37553 		if( c2 > 1 ){
37554 			pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
37555 		}
37556 	}
37557 	pData->iCount++;
37558 	return JX9_OK;
37559 }
37560 /*
37561  * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]])
37562  *  Format line as CSV and write to file pointer.
37563  * Parameters
37564  *  $handle
37565  *   Open file handle.
37566  * $fields
37567  *   An array of values.
37568  * $delimiter
37569  *   The optional delimiter parameter sets the field delimiter (one character only).
37570  * $enclosure
37571  *  The optional enclosure parameter sets the field enclosure (one character only).
37572  */
jx9Builtin_fputcsv(jx9_context * pCtx,int nArg,jx9_value ** apArg)37573 static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
37574 {
37575 	const jx9_io_stream *pStream;
37576 	struct csv_data sCsv;
37577 	io_private *pDev;
37578 	char *zEol;
37579 	int eolen;
37580 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
37581 		/* Missing/Invalid arguments, return FALSE */
37582 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
37583 		jx9_result_bool(pCtx, 0);
37584 		return JX9_OK;
37585 	}
37586 	/* Extract our private data */
37587 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37588 	/* Make sure we are dealing with a valid io_private instance */
37589 	if( IO_PRIVATE_INVALID(pDev) ){
37590 		/*Expecting an IO handle */
37591 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37592 		jx9_result_bool(pCtx, 0);
37593 		return JX9_OK;
37594 	}
37595 	/* Point to the target IO stream device */
37596 	pStream = pDev->pStream;
37597 	if( pStream == 0  || pStream->xWrite == 0){
37598 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37599 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
37600 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
37601 			);
37602 		jx9_result_bool(pCtx, 0);
37603 		return JX9_OK;
37604 	}
37605 	/* Set default csv separator */
37606 	sCsv.delimiter = ',';
37607 	sCsv.enclosure = '"';
37608 	sCsv.pDev = pDev;
37609 	sCsv.iCount = 0;
37610 	if( nArg > 2 ){
37611 		/* User delimiter */
37612 		const char *z;
37613 		int n;
37614 		z = jx9_value_to_string(apArg[2], &n);
37615 		if( n > 0 ){
37616 			sCsv.delimiter = z[0];
37617 		}
37618 		if( nArg > 3 ){
37619 			z = jx9_value_to_string(apArg[3], &n);
37620 			if( n > 0 ){
37621 				sCsv.enclosure = z[0];
37622 			}
37623 		}
37624 	}
37625 	/* Iterate throw array entries and write csv data */
37626 	jx9_array_walk(apArg[1], csv_write_callback, &sCsv);
37627 	/* Write a line ending */
37628 #ifdef __WINNT__
37629 	zEol = "\r\n";
37630 	eolen = (int)sizeof("\r\n")-1;
37631 #else
37632 	/* Assume UNIX LF */
37633 	zEol = "\n";
37634 	eolen = (int)sizeof(char);
37635 #endif
37636 	pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen);
37637 	return JX9_OK;
37638 }
37639 /*
37640  * fprintf, vfprintf private data.
37641  * An instance of the following structure is passed to the formatted
37642  * input consumer callback defined below.
37643  */
37644 typedef struct fprintf_data fprintf_data;
37645 struct fprintf_data
37646 {
37647 	io_private *pIO;        /* IO stream */
37648 	jx9_int64 nCount;       /* Total number of bytes written */
37649 };
37650 /*
37651  * Callback [i.e: Formatted input consumer] for the fprintf function.
37652  */
fprintfConsumer(jx9_context * pCtx,const char * zInput,int nLen,void * pUserData)37653 static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
37654 {
37655 	fprintf_data *pFdata = (fprintf_data *)pUserData;
37656 	jx9_int64 n;
37657 	/* Write the formatted data */
37658 	n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen);
37659 	if( n < 1 ){
37660 		SXUNUSED(pCtx); /* cc warning */
37661 		/* IO error, abort immediately */
37662 		return SXERR_ABORT;
37663 	}
37664 	/* Increment counter */
37665 	pFdata->nCount += n;
37666 	return JX9_OK;
37667 }
37668 /*
37669  * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]])
37670  *  Write a formatted string to a stream.
37671  * Parameters
37672  *  $handle
37673  *   The file pointer.
37674  *  $format
37675  *   String format (see sprintf()).
37676  * Return
37677  *  The length of the written string.
37678  */
jx9Builtin_fprintf(jx9_context * pCtx,int nArg,jx9_value ** apArg)37679 static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
37680 {
37681 	fprintf_data sFdata;
37682 	const char *zFormat;
37683 	io_private *pDev;
37684 	int nLen;
37685 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
37686 		/* Missing/Invalid arguments, return zero */
37687 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
37688 		jx9_result_int(pCtx, 0);
37689 		return JX9_OK;
37690 	}
37691 	/* Extract our private data */
37692 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37693 	/* Make sure we are dealing with a valid io_private instance */
37694 	if( IO_PRIVATE_INVALID(pDev) ){
37695 		/*Expecting an IO handle */
37696 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37697 		jx9_result_int(pCtx, 0);
37698 		return JX9_OK;
37699 	}
37700 	/* Point to the target IO stream device */
37701 	if( pDev->pStream == 0  || pDev->pStream->xWrite == 0 ){
37702 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37703 			"IO routine(%s) not implemented in the underlying stream(%s) device",
37704 			jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
37705 			);
37706 		jx9_result_int(pCtx, 0);
37707 		return JX9_OK;
37708 	}
37709 	/* Extract the string format */
37710 	zFormat = jx9_value_to_string(apArg[1], &nLen);
37711 	if( nLen < 1 ){
37712 		/* Empty string, return zero */
37713 		jx9_result_int(pCtx, 0);
37714 		return JX9_OK;
37715 	}
37716 	/* Prepare our private data */
37717 	sFdata.nCount = 0;
37718 	sFdata.pIO = pDev;
37719 	/* Format the string */
37720 	jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
37721 	/* Return total number of bytes written */
37722 	jx9_result_int64(pCtx, sFdata.nCount);
37723 	return JX9_OK;
37724 }
37725 /*
37726  * int vfprintf(resource $handle, string $format, array $args)
37727  *  Write a formatted string to a stream.
37728  * Parameters
37729  *  $handle
37730  *   The file pointer.
37731  *  $format
37732  *   String format (see sprintf()).
37733  * $args
37734  *   User arguments.
37735  * Return
37736  *  The length of the written string.
37737  */
jx9Builtin_vfprintf(jx9_context * pCtx,int nArg,jx9_value ** apArg)37738 static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
37739 {
37740 	fprintf_data sFdata;
37741 	const char *zFormat;
37742 	jx9_hashmap *pMap;
37743 	io_private *pDev;
37744 	SySet sArg;
37745 	int n, nLen;
37746 	if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1])  || !jx9_value_is_json_array(apArg[2]) ){
37747 		/* Missing/Invalid arguments, return zero */
37748 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
37749 		jx9_result_int(pCtx, 0);
37750 		return JX9_OK;
37751 	}
37752 	/* Extract our private data */
37753 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
37754 	/* Make sure we are dealing with a valid io_private instance */
37755 	if( IO_PRIVATE_INVALID(pDev) ){
37756 		/*Expecting an IO handle */
37757 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
37758 		jx9_result_int(pCtx, 0);
37759 		return JX9_OK;
37760 	}
37761 	/* Point to the target IO stream device */
37762 	if( pDev->pStream == 0  || pDev->pStream->xWrite == 0 ){
37763 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37764 			"IO routine(%s) not implemented in the underlying stream(%s) device",
37765 			jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
37766 			);
37767 		jx9_result_int(pCtx, 0);
37768 		return JX9_OK;
37769 	}
37770 	/* Extract the string format */
37771 	zFormat = jx9_value_to_string(apArg[1], &nLen);
37772 	if( nLen < 1 ){
37773 		/* Empty string, return zero */
37774 		jx9_result_int(pCtx, 0);
37775 		return JX9_OK;
37776 	}
37777 	/* Point to hashmap */
37778 	pMap = (jx9_hashmap *)apArg[2]->x.pOther;
37779 	/* Extract arguments from the hashmap */
37780 	n = jx9HashmapValuesToSet(pMap, &sArg);
37781 	/* Prepare our private data */
37782 	sFdata.nCount = 0;
37783 	sFdata.pIO = pDev;
37784 	/* Format the string */
37785 	jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
37786 	/* Return total number of bytes written*/
37787 	jx9_result_int64(pCtx, sFdata.nCount);
37788 	SySetRelease(&sArg);
37789 	return JX9_OK;
37790 }
37791 /*
37792  * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
37793  * According to the JX9 reference manual:
37794  *  The mode parameter specifies the type of access you require to the stream. It may be any of the following
37795  *   'r' 	Open for reading only; place the file pointer at the beginning of the file.
37796  *   'r+' 	Open for reading and writing; place the file pointer at the beginning of the file.
37797  *   'w' 	Open for writing only; place the file pointer at the beginning of the file and truncate the file
37798  *          to zero length. If the file does not exist, attempt to create it.
37799  *   'w+' 	Open for reading and writing; place the file pointer at the beginning of the file and truncate
37800  *              the file to zero length. If the file does not exist, attempt to create it.
37801  *   'a' 	Open for writing only; place the file pointer at the end of the file. If the file does not
37802  *         exist, attempt to create it.
37803  *   'a+' 	Open for reading and writing; place the file pointer at the end of the file. If the file does
37804  *          not exist, attempt to create it.
37805  *   'x' 	Create and open for writing only; place the file pointer at the beginning of the file. If the file
37806  *         already exists,
37807  *         the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file
37808  *         does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for
37809  *         the underlying open(2) system call.
37810  *   'x+' 	Create and open for reading and writing; otherwise it has the same behavior as 'x'.
37811  *   'c' 	Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated
37812  *          (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer
37813  *          is positioned on the beginning of the file.
37814  *          This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file
37815  *          as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can
37816  *          be used after the lock is requested).
37817  *   'c+' 	Open the file for reading and writing; otherwise it has the same behavior as 'c'.
37818  */
StrModeToFlags(jx9_context * pCtx,const char * zMode,int nLen)37819 static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen)
37820 {
37821 	const char *zEnd = &zMode[nLen];
37822 	int iFlag = 0;
37823 	int c;
37824 	if( nLen < 1 ){
37825 		/* Open in a read-only mode */
37826 		return JX9_IO_OPEN_RDONLY;
37827 	}
37828 	c = zMode[0];
37829 	if( c == 'r' || c == 'R' ){
37830 		/* Read-only access */
37831 		iFlag = JX9_IO_OPEN_RDONLY;
37832 		zMode++; /* Advance */
37833 		if( zMode < zEnd ){
37834 			c = zMode[0];
37835 			if( c == '+' || c == 'w' || c == 'W' ){
37836 				/* Read+Write access */
37837 				iFlag = JX9_IO_OPEN_RDWR;
37838 			}
37839 		}
37840 	}else if( c == 'w' || c == 'W' ){
37841 		/* Overwrite mode.
37842 		 * If the file does not exists, try to create it
37843 		 */
37844 		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
37845 		zMode++; /* Advance */
37846 		if( zMode < zEnd ){
37847 			c = zMode[0];
37848 			if( c == '+' || c == 'r' || c == 'R' ){
37849 				/* Read+Write access */
37850 				iFlag &= ~JX9_IO_OPEN_WRONLY;
37851 				iFlag |= JX9_IO_OPEN_RDWR;
37852 			}
37853 		}
37854 	}else if( c == 'a' || c == 'A' ){
37855 		/* Append mode (place the file pointer at the end of the file).
37856 		 * Create the file if it does not exists.
37857 		 */
37858 		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
37859 		zMode++; /* Advance */
37860 		if( zMode < zEnd ){
37861 			c = zMode[0];
37862 			if( c == '+' ){
37863 				/* Read-Write access */
37864 				iFlag &= ~JX9_IO_OPEN_WRONLY;
37865 				iFlag |= JX9_IO_OPEN_RDWR;
37866 			}
37867 		}
37868 	}else if( c == 'x' || c == 'X' ){
37869 		/* Exclusive access.
37870 		 * If the file already exists, return immediately with a failure code.
37871 		 * Otherwise create a new file.
37872 		 */
37873 		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
37874 		zMode++; /* Advance */
37875 		if( zMode < zEnd ){
37876 			c = zMode[0];
37877 			if( c == '+' || c == 'r' || c == 'R' ){
37878 				/* Read-Write access */
37879 				iFlag &= ~JX9_IO_OPEN_WRONLY;
37880 				iFlag |= JX9_IO_OPEN_RDWR;
37881 			}
37882 		}
37883 	}else if( c == 'c' || c == 'C' ){
37884 		/* Overwrite mode.Create the file if it does not exists.*/
37885 		iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
37886 		zMode++; /* Advance */
37887 		if( zMode < zEnd ){
37888 			c = zMode[0];
37889 			if( c == '+' ){
37890 				/* Read-Write access */
37891 				iFlag &= ~JX9_IO_OPEN_WRONLY;
37892 				iFlag |= JX9_IO_OPEN_RDWR;
37893 			}
37894 		}
37895 	}else{
37896 		/* Invalid mode. Assume a read only open */
37897 		jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
37898 		iFlag = JX9_IO_OPEN_RDONLY;
37899 	}
37900 	while( zMode < zEnd ){
37901 		c = zMode[0];
37902 		if( c == 'b' || c == 'B' ){
37903 			iFlag &= ~JX9_IO_OPEN_TEXT;
37904 			iFlag |= JX9_IO_OPEN_BINARY;
37905 		}else if( c == 't' || c == 'T' ){
37906 			iFlag &= ~JX9_IO_OPEN_BINARY;
37907 			iFlag |= JX9_IO_OPEN_TEXT;
37908 		}
37909 		zMode++;
37910 	}
37911 	return iFlag;
37912 }
37913 /*
37914  * Initialize the IO private structure.
37915  */
InitIOPrivate(jx9_vm * pVm,const jx9_io_stream * pStream,io_private * pOut)37916 static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut)
37917 {
37918 	pOut->pStream = pStream;
37919 	SyBlobInit(&pOut->sBuffer, &pVm->sAllocator);
37920 	pOut->nOfft = 0;
37921 	/* Set the magic number */
37922 	pOut->iMagic = IO_PRIVATE_MAGIC;
37923 }
37924 /*
37925  * Release the IO private structure.
37926  */
ReleaseIOPrivate(jx9_context * pCtx,io_private * pDev)37927 static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev)
37928 {
37929 	SyBlobRelease(&pDev->sBuffer);
37930 	pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
37931 	/* Release the whole structure */
37932 	jx9_context_free_chunk(pCtx, pDev);
37933 }
37934 /*
37935  * Reset the IO private structure.
37936  */
ResetIOPrivate(io_private * pDev)37937 static void ResetIOPrivate(io_private *pDev)
37938 {
37939 	SyBlobReset(&pDev->sBuffer);
37940 	pDev->nOfft = 0;
37941 }
37942 /* Forward declaration */
37943 static int is_jx9_stream(const jx9_io_stream *pStream);
37944 /*
37945  * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]])
37946  *  Open a file, a URL or any other IO stream.
37947  * Parameters
37948  *  $filename
37949  *   If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search
37950  *   for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given
37951  *   then a regular file is assumed.
37952  *  $mode
37953  *   The mode parameter specifies the type of access you require to the stream
37954  *   See the block comment associated with the StrModeToFlags() for the supported
37955  *   modes.
37956  *  $use_include_path
37957  *   You can use the optional second parameter and set it to
37958  *   TRUE, if you want to search for the file in the include_path, too.
37959  *  $context
37960  *   A context stream resource.
37961  * Return
37962  *  File handle on success or FALSE on failure.
37963  */
jx9Builtin_fopen(jx9_context * pCtx,int nArg,jx9_value ** apArg)37964 static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
37965 {
37966 	const jx9_io_stream *pStream;
37967 	const char *zUri, *zMode;
37968 	jx9_value *pResource;
37969 	io_private *pDev;
37970 	int iLen, imLen;
37971 	int iOpenFlags;
37972 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
37973 		/* Missing/Invalid arguments, return FALSE */
37974 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
37975 		jx9_result_bool(pCtx, 0);
37976 		return JX9_OK;
37977 	}
37978 	/* Extract the URI and the desired access mode */
37979 	zUri  = jx9_value_to_string(apArg[0], &iLen);
37980 	if( nArg > 1 ){
37981 		zMode = jx9_value_to_string(apArg[1], &imLen);
37982 	}else{
37983 		/* Set a default read-only mode */
37984 		zMode = "r";
37985 		imLen = (int)sizeof(char);
37986 	}
37987 	/* Try to extract a stream */
37988 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
37989 	if( pStream == 0 ){
37990 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
37991 			"No stream device is associated with the given URI(%s)", zUri);
37992 		jx9_result_bool(pCtx, 0);
37993 		return JX9_OK;
37994 	}
37995 	/* Allocate a new IO private instance */
37996 	pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
37997 	if( pDev == 0 ){
37998 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
37999 		jx9_result_bool(pCtx, 0);
38000 		return JX9_OK;
38001 	}
38002 	pResource = 0;
38003 	if( nArg > 3 ){
38004 		pResource = apArg[3];
38005 	}else if( is_jx9_stream(pStream) ){
38006 		/* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
38007 		 * virtual machine.
38008 		 */
38009 		pResource = apArg[0];
38010 	}
38011 	/* Initialize the structure */
38012 	InitIOPrivate(pCtx->pVm, pStream, pDev);
38013 	/* Convert open mode to JX9 flags */
38014 	iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
38015 	/* Try to get a handle */
38016 	pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags,
38017 		nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
38018 	if( pDev->pHandle == 0 ){
38019 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
38020 		jx9_result_bool(pCtx, 0);
38021 		jx9_context_free_chunk(pCtx, pDev);
38022 		return JX9_OK;
38023 	}
38024 	/* All done, return the io_private instance as a resource */
38025 	jx9_result_resource(pCtx, pDev);
38026 	return JX9_OK;
38027 }
38028 /*
38029  * bool fclose(resource $handle)
38030  *  Closes an open file pointer
38031  * Parameters
38032  *  $handle
38033  *   The file pointer.
38034  * Return
38035  *  TRUE on success or FALSE on failure.
38036  */
jx9Builtin_fclose(jx9_context * pCtx,int nArg,jx9_value ** apArg)38037 static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
38038 {
38039 	const jx9_io_stream *pStream;
38040 	io_private *pDev;
38041 	jx9_vm *pVm;
38042 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38043 		/* Missing/Invalid arguments, return FALSE */
38044 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
38045 		jx9_result_bool(pCtx, 0);
38046 		return JX9_OK;
38047 	}
38048 	/* Extract our private data */
38049 	pDev = (io_private *)jx9_value_to_resource(apArg[0]);
38050 	/* Make sure we are dealing with a valid io_private instance */
38051 	if( IO_PRIVATE_INVALID(pDev) ){
38052 		/*Expecting an IO handle */
38053 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
38054 		jx9_result_bool(pCtx, 0);
38055 		return JX9_OK;
38056 	}
38057 	/* Point to the target IO stream device */
38058 	pStream = pDev->pStream;
38059 	if( pStream == 0 ){
38060 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
38061 			"IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
38062 			jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
38063 			);
38064 		jx9_result_bool(pCtx, 0);
38065 		return JX9_OK;
38066 	}
38067 	/* Point to the VM that own this context */
38068 	pVm = pCtx->pVm;
38069 	/* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */
38070 	if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){
38071 		/* Perform the requested operation */
38072 		jx9StreamCloseHandle(pStream, pDev->pHandle);
38073 		/* Release the IO private structure */
38074 		ReleaseIOPrivate(pCtx, pDev);
38075 		/* Invalidate the resource handle */
38076 		jx9_value_release(apArg[0]);
38077 	}
38078 	/* Return TRUE */
38079 	jx9_result_bool(pCtx, 1);
38080 	return JX9_OK;
38081 }
38082 #if !defined(JX9_DISABLE_HASH_FUNC)
38083 /*
38084  * MD5/SHA1 digest consumer.
38085  */
vfsHashConsumer(const void * pData,unsigned int nLen,void * pUserData)38086 static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
38087 {
38088 	/* Append hex chunk verbatim */
38089 	jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
38090 	return SXRET_OK;
38091 }
38092 /*
38093  * string md5_file(string $uri[, bool $raw_output = false ])
38094  *  Calculates the md5 hash of a given file.
38095  * Parameters
38096  *  $uri
38097  *   Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
38098  *  $raw_output
38099  *   When TRUE, returns the digest in raw binary format with a length of 16.
38100  * Return
38101  *  Return the MD5 digest on success or FALSE on failure.
38102  */
jx9Builtin_md5_file(jx9_context * pCtx,int nArg,jx9_value ** apArg)38103 static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38104 {
38105 	const jx9_io_stream *pStream;
38106 	unsigned char zDigest[16];
38107 	int raw_output  = FALSE;
38108 	const char *zFile;
38109 	MD5Context sCtx;
38110 	char zBuf[8192];
38111 	void *pHandle;
38112 	jx9_int64 n;
38113 	int nLen;
38114 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38115 		/* Missing/Invalid arguments, return FALSE */
38116 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38117 		jx9_result_bool(pCtx, 0);
38118 		return JX9_OK;
38119 	}
38120 	/* Extract the file path */
38121 	zFile = jx9_value_to_string(apArg[0], &nLen);
38122 	/* Point to the target IO stream device */
38123 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38124 	if( pStream == 0 ){
38125 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38126 		jx9_result_bool(pCtx, 0);
38127 		return JX9_OK;
38128 	}
38129 	if( nArg > 1 ){
38130 		raw_output = jx9_value_to_bool(apArg[1]);
38131 	}
38132 	/* Try to open the file in read-only mode */
38133 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38134 	if( pHandle == 0 ){
38135 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38136 		jx9_result_bool(pCtx, 0);
38137 		return JX9_OK;
38138 	}
38139 	/* Init the MD5 context */
38140 	MD5Init(&sCtx);
38141 	/* Perform the requested operation */
38142 	for(;;){
38143 		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
38144 		if( n < 1 ){
38145 			/* EOF or IO error, break immediately */
38146 			break;
38147 		}
38148 		MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
38149 	}
38150 	/* Close the stream */
38151 	jx9StreamCloseHandle(pStream, pHandle);
38152 	/* Extract the digest */
38153 	MD5Final(zDigest, &sCtx);
38154 	if( raw_output ){
38155 		/* Output raw digest */
38156 		jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
38157 	}else{
38158 		/* Perform a binary to hex conversion */
38159 		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
38160 	}
38161 	return JX9_OK;
38162 }
38163 /*
38164  * string sha1_file(string $uri[, bool $raw_output = false ])
38165  *  Calculates the SHA1 hash of a given file.
38166  * Parameters
38167  *  $uri
38168  *   Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
38169  *  $raw_output
38170  *   When TRUE, returns the digest in raw binary format with a length of 20.
38171  * Return
38172  *  Return the SHA1 digest on success or FALSE on failure.
38173  */
jx9Builtin_sha1_file(jx9_context * pCtx,int nArg,jx9_value ** apArg)38174 static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38175 {
38176 	const jx9_io_stream *pStream;
38177 	unsigned char zDigest[20];
38178 	int raw_output  = FALSE;
38179 	const char *zFile;
38180 	SHA1Context sCtx;
38181 	char zBuf[8192];
38182 	void *pHandle;
38183 	jx9_int64 n;
38184 	int nLen;
38185 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38186 		/* Missing/Invalid arguments, return FALSE */
38187 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38188 		jx9_result_bool(pCtx, 0);
38189 		return JX9_OK;
38190 	}
38191 	/* Extract the file path */
38192 	zFile = jx9_value_to_string(apArg[0], &nLen);
38193 	/* Point to the target IO stream device */
38194 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38195 	if( pStream == 0 ){
38196 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38197 		jx9_result_bool(pCtx, 0);
38198 		return JX9_OK;
38199 	}
38200 	if( nArg > 1 ){
38201 		raw_output = jx9_value_to_bool(apArg[1]);
38202 	}
38203 	/* Try to open the file in read-only mode */
38204 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38205 	if( pHandle == 0 ){
38206 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38207 		jx9_result_bool(pCtx, 0);
38208 		return JX9_OK;
38209 	}
38210 	/* Init the SHA1 context */
38211 	SHA1Init(&sCtx);
38212 	/* Perform the requested operation */
38213 	for(;;){
38214 		n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
38215 		if( n < 1 ){
38216 			/* EOF or IO error, break immediately */
38217 			break;
38218 		}
38219 		SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
38220 	}
38221 	/* Close the stream */
38222 	jx9StreamCloseHandle(pStream, pHandle);
38223 	/* Extract the digest */
38224 	SHA1Final(&sCtx, zDigest);
38225 	if( raw_output ){
38226 		/* Output raw digest */
38227 		jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
38228 	}else{
38229 		/* Perform a binary to hex conversion */
38230 		SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
38231 	}
38232 	return JX9_OK;
38233 }
38234 #endif /* JX9_DISABLE_HASH_FUNC */
38235 /*
38236  * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] )
38237  *  Parse a configuration file.
38238  * Parameters
38239  * $filename
38240  *  The filename of the ini file being parsed.
38241  * $process_sections
38242  *  By setting the process_sections parameter to TRUE, you get a multidimensional array
38243  *  with the section names and settings included.
38244  *  The default for process_sections is FALSE.
38245  * $scanner_mode
38246  *  Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW.
38247  *  If INI_SCANNER_RAW is supplied, then option values will not be parsed.
38248  * Return
38249  *  The settings are returned as an associative array on success.
38250  *  Otherwise is returned.
38251  */
jx9Builtin_parse_ini_file(jx9_context * pCtx,int nArg,jx9_value ** apArg)38252 static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
38253 {
38254 	const jx9_io_stream *pStream;
38255 	const char *zFile;
38256 	SyBlob sContents;
38257 	void *pHandle;
38258 	int nLen;
38259 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38260 		/* Missing/Invalid arguments, return FALSE */
38261 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38262 		jx9_result_bool(pCtx, 0);
38263 		return JX9_OK;
38264 	}
38265 	/* Extract the file path */
38266 	zFile = jx9_value_to_string(apArg[0], &nLen);
38267 	/* Point to the target IO stream device */
38268 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38269 	if( pStream == 0 ){
38270 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38271 		jx9_result_bool(pCtx, 0);
38272 		return JX9_OK;
38273 	}
38274 	/* Try to open the file in read-only mode */
38275 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38276 	if( pHandle == 0 ){
38277 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38278 		jx9_result_bool(pCtx, 0);
38279 		return JX9_OK;
38280 	}
38281 	SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
38282 	/* Read the whole file */
38283 	jx9StreamReadWholeFile(pHandle, pStream, &sContents);
38284 	if( SyBlobLength(&sContents) < 1 ){
38285 		/* Empty buffer, return FALSE */
38286 		jx9_result_bool(pCtx, 0);
38287 	}else{
38288 		/* Process the raw INI buffer */
38289 		jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents),
38290 			nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
38291 	}
38292 	/* Close the stream */
38293 	jx9StreamCloseHandle(pStream, pHandle);
38294 	/* Release the working buffer */
38295 	SyBlobRelease(&sContents);
38296 	return JX9_OK;
38297 }
38298 /*
38299  * Section:
38300  *    ZIP archive processing.
38301  * Authors:
38302  *    Symisc Systems, devel@symisc.net.
38303  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
38304  * Status:
38305  *    Stable.
38306  */
38307 typedef struct zip_raw_data zip_raw_data;
38308 struct zip_raw_data
38309 {
38310 	int iType;         /* Where the raw data is stored */
38311 	union raw_data{
38312 		struct mmap_data{
38313 			void *pMap;          /* Memory mapped data */
38314 			jx9_int64 nSize;     /* Map size */
38315 			const jx9_vfs *pVfs; /* Underlying vfs */
38316 		}mmap;
38317 		SyBlob sBlob;  /* Memory buffer */
38318 	}raw;
38319 };
38320 #define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */
38321 #define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically
38322                                * allocated memory chunk.
38323 							   */
38324  /*
38325   * mixed zip_open(string $filename)
38326   *  Opens a new zip archive for reading.
38327   * Parameters
38328   *  $filename
38329   *   The file name of the ZIP archive to open.
38330   * Return
38331   *  A resource handle for later use with zip_read() and zip_close() or FALSE on failure.
38332   */
jx9Builtin_zip_open(jx9_context * pCtx,int nArg,jx9_value ** apArg)38333 static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
38334 {
38335 	const jx9_io_stream *pStream;
38336 	SyArchive *pArchive;
38337 	zip_raw_data *pRaw;
38338 	const char *zFile;
38339 	SyBlob *pContents;
38340 	void *pHandle;
38341 	int nLen;
38342 	sxi32 rc;
38343 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
38344 		/* Missing/Invalid arguments, return FALSE */
38345 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
38346 		jx9_result_bool(pCtx, 0);
38347 		return JX9_OK;
38348 	}
38349 	/* Extract the file path */
38350 	zFile = jx9_value_to_string(apArg[0], &nLen);
38351 	/* Point to the target IO stream device */
38352 	pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
38353 	if( pStream == 0 ){
38354 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
38355 		jx9_result_bool(pCtx, 0);
38356 		return JX9_OK;
38357 	}
38358 	/* Create an in-memory archive */
38359 	pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
38360 	if( pArchive == 0 ){
38361 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
38362 		jx9_result_bool(pCtx, 0);
38363 		return JX9_OK;
38364 	}
38365 	pRaw = (zip_raw_data *)&pArchive[1];
38366 	/* Initialize the archive */
38367 	SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0);
38368 	/* Extract the default stream */
38369 	if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){
38370 		const jx9_vfs *pVfs;
38371 		/* Try to get a memory view of the whole file since ZIP files
38372 		 * tends to be very big this days, this is a huge performance win.
38373 		 */
38374 		pVfs = jx9ExportBuiltinVfs();
38375 		if( pVfs && pVfs->xMmap ){
38376 			rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
38377 			if( rc == JX9_OK ){
38378 				/* Nice, Extract the whole archive */
38379 				rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize);
38380 				if( rc != SXRET_OK ){
38381 					if( pVfs->xUnmap ){
38382 						pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
38383 					}
38384 					/* Release the allocated chunk */
38385 					jx9_context_free_chunk(pCtx, pArchive);
38386 					/* Something goes wrong with this ZIP archive, return FALSE */
38387 					jx9_result_bool(pCtx, 0);
38388 					return JX9_OK;
38389 				}
38390 				/* Archive successfully opened */
38391 				pRaw->iType = ZIP_RAW_DATA_MMAPED;
38392 				pRaw->raw.mmap.pVfs = pVfs;
38393 				goto success;
38394 			}
38395 		}
38396 		/* FALL THROUGH */
38397 	}
38398 	/* Try to open the file in read-only mode */
38399 	pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
38400 	if( pHandle == 0 ){
38401 		jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
38402 		jx9_result_bool(pCtx, 0);
38403 		return JX9_OK;
38404 	}
38405 	pContents = &pRaw->raw.sBlob;
38406 	SyBlobInit(pContents, &pCtx->pVm->sAllocator);
38407 	/* Read the whole file */
38408 	jx9StreamReadWholeFile(pHandle, pStream, pContents);
38409 	/* Assume an invalid ZIP file */
38410 	rc = SXERR_INVALID;
38411 	if( SyBlobLength(pContents) > 0 ){
38412 		/* Extract archive entries */
38413 		rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents));
38414 	}
38415 	pRaw->iType = ZIP_RAW_DATA_MEMBUF;
38416 	/* Close the stream */
38417 	jx9StreamCloseHandle(pStream, pHandle);
38418 	if( rc != SXRET_OK ){
38419 		/* Release the working buffer */
38420 		SyBlobRelease(pContents);
38421 		/* Release the allocated chunk */
38422 		jx9_context_free_chunk(pCtx, pArchive);
38423 		/* Something goes wrong with this ZIP archive, return FALSE */
38424 		jx9_result_bool(pCtx, 0);
38425 		return JX9_OK;
38426 	}
38427 success:
38428 	/* Reset the loop cursor */
38429 	SyArchiveResetLoopCursor(pArchive);
38430 	/* Return the in-memory archive as a resource handle */
38431 	jx9_result_resource(pCtx, pArchive);
38432 	return JX9_OK;
38433 }
38434 /*
38435   * void zip_close(resource $zip)
38436   *  Close an in-memory ZIP archive.
38437   * Parameters
38438   *  $zip
38439   *   A ZIP file previously opened with zip_open().
38440   * Return
38441   *  null.
38442   */
jx9Builtin_zip_close(jx9_context * pCtx,int nArg,jx9_value ** apArg)38443 static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
38444 {
38445 	SyArchive *pArchive;
38446 	zip_raw_data *pRaw;
38447 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38448 		/* Missing/Invalid arguments */
38449 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38450 		return JX9_OK;
38451 	}
38452 	/* Point to the in-memory archive */
38453 	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38454 	/* Make sure we are dealing with a valid ZIP archive */
38455 	if( SXARCH_INVALID(pArchive) ){
38456 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38457 		return JX9_OK;
38458 	}
38459 	/* Release the archive */
38460 	SyArchiveRelease(pArchive);
38461 	pRaw = (zip_raw_data *)&pArchive[1];
38462 	if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
38463 		SyBlobRelease(&pRaw->raw.sBlob);
38464 	}else{
38465 		const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs;
38466 		if( pVfs->xUnmap ){
38467 			/* Unmap the memory view */
38468 			pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
38469 		}
38470 	}
38471 	/* Release the memory chunk */
38472 	jx9_context_free_chunk(pCtx, pArchive);
38473 	return JX9_OK;
38474 }
38475 /*
38476   * mixed zip_read(resource $zip)
38477   *  Reads the next entry from an in-memory ZIP archive.
38478   * Parameters
38479   *  $zip
38480   *   A ZIP file previously opened with zip_open().
38481   * Return
38482   *  A directory entry resource for later use with the zip_entry_... functions
38483   *  or FALSE if there are no more entries to read, or an error code if an error occurred.
38484   */
jx9Builtin_zip_read(jx9_context * pCtx,int nArg,jx9_value ** apArg)38485 static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
38486 {
38487 	SyArchiveEntry *pNext = 0; /* cc warning */
38488 	SyArchive *pArchive;
38489 	sxi32 rc;
38490 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38491 		/* Missing/Invalid arguments */
38492 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38493 		/* return FALSE */
38494 		jx9_result_bool(pCtx, 0);
38495 		return JX9_OK;
38496 	}
38497 	/* Point to the in-memory archive */
38498 	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38499 	/* Make sure we are dealing with a valid ZIP archive */
38500 	if( SXARCH_INVALID(pArchive) ){
38501 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38502 		/* return FALSE */
38503 		jx9_result_bool(pCtx, 0);
38504 		return JX9_OK;
38505 	}
38506 	/* Extract the next entry */
38507 	rc = SyArchiveGetNextEntry(pArchive, &pNext);
38508 	if( rc != SXRET_OK ){
38509 		/* No more entries in the central directory, return FALSE */
38510 		jx9_result_bool(pCtx, 0);
38511 	}else{
38512 		/* Return as a resource handle */
38513 		jx9_result_resource(pCtx, pNext);
38514 		/* Point to the ZIP raw data */
38515 		pNext->pUserData = (void *)&pArchive[1];
38516 	}
38517 	return JX9_OK;
38518 }
38519 /*
38520   * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ])
38521   *  Open a directory entry for reading
38522   * Parameters
38523   *  $zip
38524   *   A ZIP file previously opened with zip_open().
38525   *  $zip_entry
38526   *   A directory entry returned by zip_read().
38527   * $mode
38528   *   Not used
38529   * Return
38530   *  A directory entry resource for later use with the zip_entry_... functions
38531   *  or FALSE if there are no more entries to read, or an error code if an error occurred.
38532   */
jx9Builtin_zip_entry_open(jx9_context * pCtx,int nArg,jx9_value ** apArg)38533 static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
38534 {
38535 	SyArchiveEntry *pEntry;
38536 	SyArchive *pArchive;
38537 	if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
38538 		/* Missing/Invalid arguments */
38539 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38540 		/* return FALSE */
38541 		jx9_result_bool(pCtx, 0);
38542 		return JX9_OK;
38543 	}
38544 	/* Point to the in-memory archive */
38545 	pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
38546 	/* Make sure we are dealing with a valid ZIP archive */
38547 	if( SXARCH_INVALID(pArchive) ){
38548 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
38549 		/* return FALSE */
38550 		jx9_result_bool(pCtx, 0);
38551 		return JX9_OK;
38552 	}
38553 	/* Make sure we are dealing with a valid ZIP archive entry */
38554 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
38555 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38556 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38557 		/* return FALSE */
38558 		jx9_result_bool(pCtx, 0);
38559 		return JX9_OK;
38560 	}
38561 	/* All done. Actually this function is a no-op, return TRUE */
38562 	jx9_result_bool(pCtx, 1);
38563 	return JX9_OK;
38564 }
38565 /*
38566   * bool zip_entry_close(resource $zip_entry)
38567   *  Close a directory entry.
38568   * Parameters
38569   *  $zip_entry
38570   *   A directory entry returned by zip_read().
38571   * Return
38572   *  Returns TRUE on success or FALSE on failure.
38573   */
jx9Builtin_zip_entry_close(jx9_context * pCtx,int nArg,jx9_value ** apArg)38574 static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
38575 {
38576 	SyArchiveEntry *pEntry;
38577 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38578 		/* Missing/Invalid arguments */
38579 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38580 		/* return FALSE */
38581 		jx9_result_bool(pCtx, 0);
38582 		return JX9_OK;
38583 	}
38584 	/* Make sure we are dealing with a valid ZIP archive entry */
38585 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38586 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38587 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38588 		/* return FALSE */
38589 		jx9_result_bool(pCtx, 0);
38590 		return JX9_OK;
38591 	}
38592 	/* Reset the read cursor */
38593 	pEntry->nReadCount = 0;
38594 	/*All done. Actually this function is a no-op, return TRUE */
38595 	jx9_result_bool(pCtx, 1);
38596 	return JX9_OK;
38597 }
38598 /*
38599   * string zip_entry_name(resource $zip_entry)
38600   *  Retrieve the name of a directory entry.
38601   * Parameters
38602   *  $zip_entry
38603   *   A directory entry returned by zip_read().
38604   * Return
38605   *  The name of the directory entry.
38606   */
jx9Builtin_zip_entry_name(jx9_context * pCtx,int nArg,jx9_value ** apArg)38607 static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
38608 {
38609 	SyArchiveEntry *pEntry;
38610 	SyString *pName;
38611 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38612 		/* Missing/Invalid arguments */
38613 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38614 		/* return FALSE */
38615 		jx9_result_bool(pCtx, 0);
38616 		return JX9_OK;
38617 	}
38618 	/* Make sure we are dealing with a valid ZIP archive entry */
38619 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38620 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38621 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38622 		/* return FALSE */
38623 		jx9_result_bool(pCtx, 0);
38624 		return JX9_OK;
38625 	}
38626 	/* Return entry name */
38627 	pName = &pEntry->sFileName;
38628 	jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
38629 	return JX9_OK;
38630 }
38631 /*
38632   * int64 zip_entry_filesize(resource $zip_entry)
38633   *  Retrieve the actual file size of a directory entry.
38634   * Parameters
38635   *  $zip_entry
38636   *   A directory entry returned by zip_read().
38637   * Return
38638   *  The size of the directory entry.
38639   */
jx9Builtin_zip_entry_filesize(jx9_context * pCtx,int nArg,jx9_value ** apArg)38640 static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
38641 {
38642 	SyArchiveEntry *pEntry;
38643 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38644 		/* Missing/Invalid arguments */
38645 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38646 		/* return FALSE */
38647 		jx9_result_bool(pCtx, 0);
38648 		return JX9_OK;
38649 	}
38650 	/* Make sure we are dealing with a valid ZIP archive entry */
38651 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38652 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38653 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38654 		/* return FALSE */
38655 		jx9_result_bool(pCtx, 0);
38656 		return JX9_OK;
38657 	}
38658 	/* Return entry size */
38659 	jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
38660 	return JX9_OK;
38661 }
38662 /*
38663   * int64 zip_entry_compressedsize(resource $zip_entry)
38664   *  Retrieve the compressed size of a directory entry.
38665   * Parameters
38666   *  $zip_entry
38667   *   A directory entry returned by zip_read().
38668   * Return
38669   *  The compressed size.
38670   */
jx9Builtin_zip_entry_compressedsize(jx9_context * pCtx,int nArg,jx9_value ** apArg)38671 static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
38672 {
38673 	SyArchiveEntry *pEntry;
38674 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38675 		/* Missing/Invalid arguments */
38676 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38677 		/* return FALSE */
38678 		jx9_result_bool(pCtx, 0);
38679 		return JX9_OK;
38680 	}
38681 	/* Make sure we are dealing with a valid ZIP archive entry */
38682 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38683 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38684 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38685 		/* return FALSE */
38686 		jx9_result_bool(pCtx, 0);
38687 		return JX9_OK;
38688 	}
38689 	/* Return entry compressed size */
38690 	jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
38691 	return JX9_OK;
38692 }
38693 /*
38694   * string zip_entry_read(resource $zip_entry[, int $length])
38695   *  Reads from an open directory entry.
38696   * Parameters
38697   *  $zip_entry
38698   *   A directory entry returned by zip_read().
38699   *  $length
38700   *   The number of bytes to return. If not specified, this function
38701   *   will attempt to read 1024 bytes.
38702   * Return
38703   *  Returns the data read, or FALSE if the end of the file is reached.
38704   */
jx9Builtin_zip_entry_read(jx9_context * pCtx,int nArg,jx9_value ** apArg)38705 static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
38706 {
38707 	SyArchiveEntry *pEntry;
38708 	zip_raw_data *pRaw;
38709 	const char *zData;
38710 	int iLength;
38711 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38712 		/* Missing/Invalid arguments */
38713 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38714 		/* return FALSE */
38715 		jx9_result_bool(pCtx, 0);
38716 		return JX9_OK;
38717 	}
38718 	/* Make sure we are dealing with a valid ZIP archive entry */
38719 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38720 	if( SXARCH_ENTRY_INVALID(pEntry) ){
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 	zData = 0;
38727 	if( pEntry->nReadCount >= pEntry->nByteCompr ){
38728 		/* No more data to read, return FALSE */
38729 		jx9_result_bool(pCtx, 0);
38730 		return JX9_OK;
38731 	}
38732 	/* Set a default read length */
38733 	iLength = 1024;
38734 	if( nArg > 1 ){
38735 		iLength = jx9_value_to_int(apArg[1]);
38736 		if( iLength < 1 ){
38737 			iLength = 1024;
38738 		}
38739 	}
38740 	if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
38741 		iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
38742 	}
38743 	/* Return the entry contents */
38744 	pRaw = (zip_raw_data *)pEntry->pUserData;
38745 	if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
38746 		zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
38747 	}else{
38748 		const char *zMap = (const char *)pRaw->raw.mmap.pMap;
38749 		/* Memory mmaped chunk */
38750 		zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
38751 	}
38752 	/* Increment the read counter */
38753 	pEntry->nReadCount += iLength;
38754 	/* Return the raw data */
38755 	jx9_result_string(pCtx, zData, iLength);
38756 	return JX9_OK;
38757 }
38758 /*
38759   * bool zip_entry_reset_cursor(resource $zip_entry)
38760   *  Reset the read cursor of an open directory entry.
38761   * Parameters
38762   *  $zip_entry
38763   *   A directory entry returned by zip_read().
38764   * Return
38765   *  TRUE on success, FALSE on failure.
38766   */
jx9Builtin_zip_entry_reset_cursor(jx9_context * pCtx,int nArg,jx9_value ** apArg)38767 static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
38768 {
38769 	SyArchiveEntry *pEntry;
38770 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38771 		/* Missing/Invalid arguments */
38772 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38773 		/* return FALSE */
38774 		jx9_result_bool(pCtx, 0);
38775 		return JX9_OK;
38776 	}
38777 	/* Make sure we are dealing with a valid ZIP archive entry */
38778 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38779 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38780 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38781 		/* return FALSE */
38782 		jx9_result_bool(pCtx, 0);
38783 		return JX9_OK;
38784 	}
38785 	/* Reset the cursor */
38786 	pEntry->nReadCount = 0;
38787 	/* Return TRUE */
38788 	jx9_result_bool(pCtx, 1);
38789 	return JX9_OK;
38790 }
38791 /*
38792   * string zip_entry_compressionmethod(resource $zip_entry)
38793   *  Retrieve the compression method of a directory entry.
38794   * Parameters
38795   *  $zip_entry
38796   *   A directory entry returned by zip_read().
38797   * Return
38798   *  The compression method on success or FALSE on failure.
38799   */
jx9Builtin_zip_entry_compressionmethod(jx9_context * pCtx,int nArg,jx9_value ** apArg)38800 static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
38801 {
38802 	SyArchiveEntry *pEntry;
38803 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
38804 		/* Missing/Invalid arguments */
38805 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38806 		/* return FALSE */
38807 		jx9_result_bool(pCtx, 0);
38808 		return JX9_OK;
38809 	}
38810 	/* Make sure we are dealing with a valid ZIP archive entry */
38811 	pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
38812 	if( SXARCH_ENTRY_INVALID(pEntry) ){
38813 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
38814 		/* return FALSE */
38815 		jx9_result_bool(pCtx, 0);
38816 		return JX9_OK;
38817 	}
38818 	switch(pEntry->nComprMeth){
38819 	case 0:
38820 		/* No compression;entry is stored */
38821 		jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
38822 		break;
38823 	case 8:
38824 		/* Entry is deflated (Default compression algorithm)  */
38825 		jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
38826 		break;
38827 		/* Exotic compression algorithms */
38828 	case 1:
38829 		jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
38830 		break;
38831 	case 2:
38832 	case 3:
38833 	case 4:
38834 	case 5:
38835 		/* Entry is reduced */
38836 		jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
38837 		break;
38838 	case 6:
38839 		/* Entry is imploded */
38840 		jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
38841 		break;
38842 	default:
38843 		jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
38844 		break;
38845 	}
38846 	return JX9_OK;
38847 }
38848 #endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
38849 /* NULL VFS [i.e: a no-op VFS]*/
38850 static const jx9_vfs null_vfs = {
38851 	"null_vfs",
38852 	JX9_VFS_VERSION,
38853 	0, /* int (*xChdir)(const char *) */
38854 	0, /* int (*xChroot)(const char *); */
38855 	0, /* int (*xGetcwd)(jx9_context *) */
38856 	0, /* int (*xMkdir)(const char *, int, int) */
38857 	0, /* int (*xRmdir)(const char *) */
38858 	0, /* int (*xIsdir)(const char *) */
38859 	0, /* int (*xRename)(const char *, const char *) */
38860 	0, /*int (*xRealpath)(const char *, jx9_context *)*/
38861 	0, /* int (*xSleep)(unsigned int) */
38862 	0, /* int (*xUnlink)(const char *) */
38863 	0, /* int (*xFileExists)(const char *) */
38864 	0, /*int (*xChmod)(const char *, int)*/
38865 	0, /*int (*xChown)(const char *, const char *)*/
38866 	0, /*int (*xChgrp)(const char *, const char *)*/
38867 	0, /* jx9_int64 (*xFreeSpace)(const char *) */
38868 	0, /* jx9_int64 (*xTotalSpace)(const char *) */
38869 	0, /* jx9_int64 (*xFileSize)(const char *) */
38870 	0, /* jx9_int64 (*xFileAtime)(const char *) */
38871 	0, /* jx9_int64 (*xFileMtime)(const char *) */
38872 	0, /* jx9_int64 (*xFileCtime)(const char *) */
38873 	0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
38874 	0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
38875 	0, /* int (*xIsfile)(const char *) */
38876 	0, /* int (*xIslink)(const char *) */
38877 	0, /* int (*xReadable)(const char *) */
38878 	0, /* int (*xWritable)(const char *) */
38879 	0, /* int (*xExecutable)(const char *) */
38880 	0, /* int (*xFiletype)(const char *, jx9_context *) */
38881 	0, /* int (*xGetenv)(const char *, jx9_context *) */
38882 	0, /* int (*xSetenv)(const char *, const char *) */
38883 	0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
38884 	0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
38885 	0, /* void (*xUnmap)(void *, jx9_int64);  */
38886 	0, /* int (*xLink)(const char *, const char *, int) */
38887 	0, /* int (*xUmask)(int) */
38888 	0, /* void (*xTempDir)(jx9_context *) */
38889 	0, /* unsigned int (*xProcessId)(void) */
38890 	0, /* int (*xUid)(void) */
38891 	0, /* int (*xGid)(void) */
38892 	0, /* void (*xUsername)(jx9_context *) */
38893 	0  /* int (*xExec)(const char *, jx9_context *) */
38894 };
38895 #ifndef JX9_DISABLE_BUILTIN_FUNC
38896 #ifndef JX9_DISABLE_DISK_IO
38897 #ifdef __WINNT__
38898 /*
38899  * Windows VFS implementation for the JX9 engine.
38900  * Authors:
38901  *    Symisc Systems, devel@symisc.net.
38902  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
38903  * Status:
38904  *    Stable.
38905  */
38906 /* What follows here is code that is specific to windows systems. */
38907 #include <Windows.h>
38908 /*
38909 ** Convert a UTF-8 string to microsoft unicode (UTF-16?).
38910 **
38911 ** Space to hold the returned string is obtained from HeapAlloc().
38912 ** Taken from the sqlite3 source tree
38913 ** status: Public Domain
38914 */
jx9utf8ToUnicode(const char * zFilename)38915 static WCHAR *jx9utf8ToUnicode(const char *zFilename){
38916   int nChar;
38917   WCHAR *zWideFilename;
38918 
38919   nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
38920   zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
38921   if( zWideFilename == 0 ){
38922  	return 0;
38923   }
38924   nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
38925   if( nChar==0 ){
38926     HeapFree(GetProcessHeap(), 0, zWideFilename);
38927     return 0;
38928   }
38929   return zWideFilename;
38930 }
38931 /*
38932 ** Convert a UTF-8 filename into whatever form the underlying
38933 ** operating system wants filenames in.Space to hold the result
38934 ** is obtained from HeapAlloc() and must be freed by the calling
38935 ** function.
38936 ** Taken from the sqlite3 source tree
38937 ** status: Public Domain
38938 */
jx9convertUtf8Filename(const char * zFilename)38939 static void *jx9convertUtf8Filename(const char *zFilename){
38940   void *zConverted;
38941   zConverted = jx9utf8ToUnicode(zFilename);
38942   return zConverted;
38943 }
38944 /*
38945 ** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
38946 ** obtained from HeapAlloc().
38947 ** Taken from the sqlite3 source tree
38948 ** status: Public Domain
38949 */
jx9unicodeToUtf8(const WCHAR * zWideFilename)38950 static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){
38951   char *zFilename;
38952   int nByte;
38953 
38954   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
38955   zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
38956   if( zFilename == 0 ){
38957   	return 0;
38958   }
38959   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
38960   if( nByte == 0 ){
38961     HeapFree(GetProcessHeap(), 0, zFilename);
38962     return 0;
38963   }
38964   return zFilename;
38965 }
38966 /* int (*xchdir)(const char *) */
WinVfs_chdir(const char * zPath)38967 static int WinVfs_chdir(const char *zPath)
38968 {
38969 	void * pConverted;
38970 	BOOL rc;
38971 	pConverted = jx9convertUtf8Filename(zPath);
38972 	if( pConverted == 0 ){
38973 		return -1;
38974 	}
38975 	rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
38976 	HeapFree(GetProcessHeap(), 0, pConverted);
38977 	return rc ? JX9_OK : -1;
38978 }
38979 /* int (*xGetcwd)(jx9_context *) */
WinVfs_getcwd(jx9_context * pCtx)38980 static int WinVfs_getcwd(jx9_context *pCtx)
38981 {
38982 	WCHAR zDir[2048];
38983 	char *zConverted;
38984 	DWORD rc;
38985 	/* Get the current directory */
38986 	rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
38987 	if( rc < 1 ){
38988 		return -1;
38989 	}
38990 	zConverted = jx9unicodeToUtf8(zDir);
38991 	if( zConverted == 0 ){
38992 		return -1;
38993 	}
38994 	jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
38995 	HeapFree(GetProcessHeap(), 0, zConverted);
38996 	return JX9_OK;
38997 }
38998 /* int (*xMkdir)(const char *, int, int) */
WinVfs_mkdir(const char * zPath,int mode,int recursive)38999 static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
39000 {
39001 	void * pConverted;
39002 	BOOL rc;
39003 	pConverted = jx9convertUtf8Filename(zPath);
39004 	if( pConverted == 0 ){
39005 		return -1;
39006 	}
39007 	mode= 0; /* MSVC warning */
39008 	recursive = 0;
39009 	rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
39010 	HeapFree(GetProcessHeap(), 0, pConverted);
39011 	return rc ? JX9_OK : -1;
39012 }
39013 /* int (*xRmdir)(const char *) */
WinVfs_rmdir(const char * zPath)39014 static int WinVfs_rmdir(const char *zPath)
39015 {
39016 	void * pConverted;
39017 	BOOL rc;
39018 	pConverted = jx9convertUtf8Filename(zPath);
39019 	if( pConverted == 0 ){
39020 		return -1;
39021 	}
39022 	rc = RemoveDirectoryW((LPCWSTR)pConverted);
39023 	HeapFree(GetProcessHeap(), 0, pConverted);
39024 	return rc ? JX9_OK : -1;
39025 }
39026 /* int (*xIsdir)(const char *) */
WinVfs_isdir(const char * zPath)39027 static int WinVfs_isdir(const char *zPath)
39028 {
39029 	void * pConverted;
39030 	DWORD dwAttr;
39031 	pConverted = jx9convertUtf8Filename(zPath);
39032 	if( pConverted == 0 ){
39033 		return -1;
39034 	}
39035 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39036 	HeapFree(GetProcessHeap(), 0, pConverted);
39037 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39038 		return -1;
39039 	}
39040 	return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
39041 }
39042 /* int (*xRename)(const char *, const char *) */
WinVfs_Rename(const char * zOld,const char * zNew)39043 static int WinVfs_Rename(const char *zOld, const char *zNew)
39044 {
39045 	void *pOld, *pNew;
39046 	BOOL rc = 0;
39047 	pOld = jx9convertUtf8Filename(zOld);
39048 	if( pOld == 0 ){
39049 		return -1;
39050 	}
39051 	pNew = jx9convertUtf8Filename(zNew);
39052 	if( pNew  ){
39053 		rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
39054 	}
39055 	HeapFree(GetProcessHeap(), 0, pOld);
39056 	if( pNew ){
39057 		HeapFree(GetProcessHeap(), 0, pNew);
39058 	}
39059 	return rc ? JX9_OK : - 1;
39060 }
39061 /* int (*xRealpath)(const char *, jx9_context *) */
WinVfs_Realpath(const char * zPath,jx9_context * pCtx)39062 static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
39063 {
39064 	WCHAR zTemp[2048];
39065 	void *pPath;
39066 	char *zReal;
39067 	DWORD n;
39068 	pPath = jx9convertUtf8Filename(zPath);
39069 	if( pPath == 0 ){
39070 		return -1;
39071 	}
39072 	n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
39073 	if( n > 0 ){
39074 		if( n >= sizeof(zTemp) ){
39075 			n = sizeof(zTemp) - 1;
39076 		}
39077 		GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
39078 	}
39079 	HeapFree(GetProcessHeap(), 0, pPath);
39080 	if( !n ){
39081 		return -1;
39082 	}
39083 	zReal = jx9unicodeToUtf8(zTemp);
39084 	if( zReal == 0 ){
39085 		return -1;
39086 	}
39087 	jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
39088 	HeapFree(GetProcessHeap(), 0, zReal);
39089 	return JX9_OK;
39090 }
39091 /* int (*xSleep)(unsigned int) */
WinVfs_Sleep(unsigned int uSec)39092 static int WinVfs_Sleep(unsigned int uSec)
39093 {
39094 	Sleep(uSec/1000/*uSec per Millisec */);
39095 	return JX9_OK;
39096 }
39097 /* int (*xUnlink)(const char *) */
WinVfs_unlink(const char * zPath)39098 static int WinVfs_unlink(const char *zPath)
39099 {
39100 	void * pConverted;
39101 	BOOL rc;
39102 	pConverted = jx9convertUtf8Filename(zPath);
39103 	if( pConverted == 0 ){
39104 		return -1;
39105 	}
39106 	rc = DeleteFileW((LPCWSTR)pConverted);
39107 	HeapFree(GetProcessHeap(), 0, pConverted);
39108 	return rc ? JX9_OK : - 1;
39109 }
39110 /* jx9_int64 (*xFreeSpace)(const char *) */
WinVfs_DiskFreeSpace(const char * zPath)39111 static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
39112 {
39113 #ifdef _WIN32_WCE
39114 	/* GetDiskFreeSpace is not supported under WINCE */
39115 	SXUNUSED(zPath);
39116 	return 0;
39117 #else
39118 	DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
39119 	void * pConverted;
39120 	WCHAR *p;
39121 	BOOL rc;
39122 	pConverted = jx9convertUtf8Filename(zPath);
39123 	if( pConverted == 0 ){
39124 		return 0;
39125 	}
39126 	p = (WCHAR *)pConverted;
39127 	for(;*p;p++){
39128 		if( *p == '\\' || *p == '/'){
39129 			*p = '\0';
39130 			break;
39131 		}
39132 	}
39133 	rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
39134 	if( !rc ){
39135 		return 0;
39136 	}
39137 	return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
39138 #endif
39139 }
39140 /* jx9_int64 (*xTotalSpace)(const char *) */
WinVfs_DiskTotalSpace(const char * zPath)39141 static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
39142 {
39143 #ifdef _WIN32_WCE
39144 	/* GetDiskFreeSpace is not supported under WINCE */
39145 	SXUNUSED(zPath);
39146 	return 0;
39147 #else
39148 	DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
39149 	void * pConverted;
39150 	WCHAR *p;
39151 	BOOL rc;
39152 	pConverted = jx9convertUtf8Filename(zPath);
39153 	if( pConverted == 0 ){
39154 		return 0;
39155 	}
39156 	p = (WCHAR *)pConverted;
39157 	for(;*p;p++){
39158 		if( *p == '\\' || *p == '/'){
39159 			*p = '\0';
39160 			break;
39161 		}
39162 	}
39163 	rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
39164 	if( !rc ){
39165 		return 0;
39166 	}
39167 	return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
39168 #endif
39169 }
39170 /* int (*xFileExists)(const char *) */
WinVfs_FileExists(const char * zPath)39171 static int WinVfs_FileExists(const char *zPath)
39172 {
39173 	void * pConverted;
39174 	DWORD dwAttr;
39175 	pConverted = jx9convertUtf8Filename(zPath);
39176 	if( pConverted == 0 ){
39177 		return -1;
39178 	}
39179 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39180 	HeapFree(GetProcessHeap(), 0, pConverted);
39181 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39182 		return -1;
39183 	}
39184 	return JX9_OK;
39185 }
39186 /* Open a file in a read-only mode */
OpenReadOnly(LPCWSTR pPath)39187 static HANDLE OpenReadOnly(LPCWSTR pPath)
39188 {
39189 	DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
39190 	DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
39191 	DWORD dwAccess = GENERIC_READ;
39192 	DWORD dwCreate = OPEN_EXISTING;
39193 	HANDLE pHandle;
39194 	pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
39195 	if( pHandle == INVALID_HANDLE_VALUE){
39196 		return 0;
39197 	}
39198 	return pHandle;
39199 }
39200 /* jx9_int64 (*xFileSize)(const char *) */
WinVfs_FileSize(const char * zPath)39201 static jx9_int64 WinVfs_FileSize(const char *zPath)
39202 {
39203 	DWORD dwLow, dwHigh;
39204 	void * pConverted;
39205 	jx9_int64 nSize;
39206 	HANDLE pHandle;
39207 
39208 	pConverted = jx9convertUtf8Filename(zPath);
39209 	if( pConverted == 0 ){
39210 		return -1;
39211 	}
39212 	/* Open the file in read-only mode */
39213 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39214 	HeapFree(GetProcessHeap(), 0, pConverted);
39215 	if( pHandle ){
39216 		dwLow = GetFileSize(pHandle, &dwHigh);
39217 		nSize = dwHigh;
39218 		nSize <<= 32;
39219 		nSize += dwLow;
39220 		CloseHandle(pHandle);
39221 	}else{
39222 		nSize = -1;
39223 	}
39224 	return nSize;
39225 }
39226 #define TICKS_PER_SECOND 10000000
39227 #define EPOCH_DIFFERENCE 11644473600LL
39228 /* Convert Windows timestamp to UNIX timestamp */
convertWindowsTimeToUnixTime(LPFILETIME pTime)39229 static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
39230 {
39231     jx9_int64 input, temp;
39232 	input = pTime->dwHighDateTime;
39233 	input <<= 32;
39234 	input += pTime->dwLowDateTime;
39235     temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
39236     temp = temp - EPOCH_DIFFERENCE;  /*subtract number of seconds between epochs*/
39237     return temp;
39238 }
39239 /* Convert UNIX timestamp to Windows timestamp */
convertUnixTimeToWindowsTime(jx9_int64 nUnixtime,LPFILETIME pOut)39240 static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
39241 {
39242   jx9_int64 result = EPOCH_DIFFERENCE;
39243   result += nUnixtime;
39244   result *= 10000000LL;
39245   pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
39246   pOut->dwLowDateTime = (DWORD)nUnixtime;
39247 }
39248 /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
WinVfs_Touch(const char * zPath,jx9_int64 touch_time,jx9_int64 access_time)39249 static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
39250 {
39251 	FILETIME sTouch, sAccess;
39252 	void *pConverted;
39253 	void *pHandle;
39254 	BOOL rc = 0;
39255 	pConverted = jx9convertUtf8Filename(zPath);
39256 	if( pConverted == 0 ){
39257 		return -1;
39258 	}
39259 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39260 	if( pHandle ){
39261 		if( touch_time < 0 ){
39262 			GetSystemTimeAsFileTime(&sTouch);
39263 		}else{
39264 			convertUnixTimeToWindowsTime(touch_time, &sTouch);
39265 		}
39266 		if( access_time < 0 ){
39267 			/* Use the touch time */
39268 			sAccess = sTouch; /* Structure assignment */
39269 		}else{
39270 			convertUnixTimeToWindowsTime(access_time, &sAccess);
39271 		}
39272 		rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
39273 		/* Close the handle */
39274 		CloseHandle(pHandle);
39275 	}
39276 	HeapFree(GetProcessHeap(), 0, pConverted);
39277 	return rc ? JX9_OK : -1;
39278 }
39279 /* jx9_int64 (*xFileAtime)(const char *) */
WinVfs_FileAtime(const char * zPath)39280 static jx9_int64 WinVfs_FileAtime(const char *zPath)
39281 {
39282 	BY_HANDLE_FILE_INFORMATION sInfo;
39283 	void * pConverted;
39284 	jx9_int64 atime;
39285 	HANDLE pHandle;
39286 	pConverted = jx9convertUtf8Filename(zPath);
39287 	if( pConverted == 0 ){
39288 		return -1;
39289 	}
39290 	/* Open the file in read-only mode */
39291 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39292 	if( pHandle ){
39293 		BOOL rc;
39294 		rc = GetFileInformationByHandle(pHandle, &sInfo);
39295 		if( rc ){
39296 			atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
39297 		}else{
39298 			atime = -1;
39299 		}
39300 		CloseHandle(pHandle);
39301 	}else{
39302 		atime = -1;
39303 	}
39304 	HeapFree(GetProcessHeap(), 0, pConverted);
39305 	return atime;
39306 }
39307 /* jx9_int64 (*xFileMtime)(const char *) */
WinVfs_FileMtime(const char * zPath)39308 static jx9_int64 WinVfs_FileMtime(const char *zPath)
39309 {
39310 	BY_HANDLE_FILE_INFORMATION sInfo;
39311 	void * pConverted;
39312 	jx9_int64 mtime;
39313 	HANDLE pHandle;
39314 	pConverted = jx9convertUtf8Filename(zPath);
39315 	if( pConverted == 0 ){
39316 		return -1;
39317 	}
39318 	/* Open the file in read-only mode */
39319 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39320 	if( pHandle ){
39321 		BOOL rc;
39322 		rc = GetFileInformationByHandle(pHandle, &sInfo);
39323 		if( rc ){
39324 			mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
39325 		}else{
39326 			mtime = -1;
39327 		}
39328 		CloseHandle(pHandle);
39329 	}else{
39330 		mtime = -1;
39331 	}
39332 	HeapFree(GetProcessHeap(), 0, pConverted);
39333 	return mtime;
39334 }
39335 /* jx9_int64 (*xFileCtime)(const char *) */
WinVfs_FileCtime(const char * zPath)39336 static jx9_int64 WinVfs_FileCtime(const char *zPath)
39337 {
39338 	BY_HANDLE_FILE_INFORMATION sInfo;
39339 	void * pConverted;
39340 	jx9_int64 ctime;
39341 	HANDLE pHandle;
39342 	pConverted = jx9convertUtf8Filename(zPath);
39343 	if( pConverted == 0 ){
39344 		return -1;
39345 	}
39346 	/* Open the file in read-only mode */
39347 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39348 	if( pHandle ){
39349 		BOOL rc;
39350 		rc = GetFileInformationByHandle(pHandle, &sInfo);
39351 		if( rc ){
39352 			ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
39353 		}else{
39354 			ctime = -1;
39355 		}
39356 		CloseHandle(pHandle);
39357 	}else{
39358 		ctime = -1;
39359 	}
39360 	HeapFree(GetProcessHeap(), 0, pConverted);
39361 	return ctime;
39362 }
39363 /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
39364 /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
WinVfs_Stat(const char * zPath,jx9_value * pArray,jx9_value * pWorker)39365 static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
39366 {
39367 	BY_HANDLE_FILE_INFORMATION sInfo;
39368 	void *pConverted;
39369 	HANDLE pHandle;
39370 	BOOL rc;
39371 	pConverted = jx9convertUtf8Filename(zPath);
39372 	if( pConverted == 0 ){
39373 		return -1;
39374 	}
39375 	/* Open the file in read-only mode */
39376 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39377 	HeapFree(GetProcessHeap(), 0, pConverted);
39378 	if( pHandle == 0 ){
39379 		return -1;
39380 	}
39381 	rc = GetFileInformationByHandle(pHandle, &sInfo);
39382 	CloseHandle(pHandle);
39383 	if( !rc ){
39384 		return -1;
39385 	}
39386 	/* dev */
39387 	jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
39388 	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
39389 	/* ino */
39390 	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
39391 	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
39392 	/* mode */
39393 	jx9_value_int(pWorker, 0);
39394 	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
39395 	/* nlink */
39396 	jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
39397 	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
39398 	/* uid, gid, rdev */
39399 	jx9_value_int(pWorker, 0);
39400 	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
39401 	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
39402 	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
39403 	/* size */
39404 	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
39405 	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
39406 	/* atime */
39407 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
39408 	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
39409 	/* mtime */
39410 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
39411 	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
39412 	/* ctime */
39413 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
39414 	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
39415 	/* blksize, blocks */
39416 	jx9_value_int(pWorker, 0);
39417 	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
39418 	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
39419 	return JX9_OK;
39420 }
39421 /* int (*xIsfile)(const char *) */
WinVfs_isfile(const char * zPath)39422 static int WinVfs_isfile(const char *zPath)
39423 {
39424 	void * pConverted;
39425 	DWORD dwAttr;
39426 	pConverted = jx9convertUtf8Filename(zPath);
39427 	if( pConverted == 0 ){
39428 		return -1;
39429 	}
39430 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39431 	HeapFree(GetProcessHeap(), 0, pConverted);
39432 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39433 		return -1;
39434 	}
39435 	return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
39436 }
39437 /* int (*xIslink)(const char *) */
WinVfs_islink(const char * zPath)39438 static int WinVfs_islink(const char *zPath)
39439 {
39440 	void * pConverted;
39441 	DWORD dwAttr;
39442 	pConverted = jx9convertUtf8Filename(zPath);
39443 	if( pConverted == 0 ){
39444 		return -1;
39445 	}
39446 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39447 	HeapFree(GetProcessHeap(), 0, pConverted);
39448 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39449 		return -1;
39450 	}
39451 	return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
39452 }
39453 /* int (*xWritable)(const char *) */
WinVfs_iswritable(const char * zPath)39454 static int WinVfs_iswritable(const char *zPath)
39455 {
39456 	void * pConverted;
39457 	DWORD dwAttr;
39458 	pConverted = jx9convertUtf8Filename(zPath);
39459 	if( pConverted == 0 ){
39460 		return -1;
39461 	}
39462 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39463 	HeapFree(GetProcessHeap(), 0, pConverted);
39464 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39465 		return -1;
39466 	}
39467 	if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
39468 		/* Not a regular file */
39469 		return -1;
39470 	}
39471 	if( dwAttr & FILE_ATTRIBUTE_READONLY ){
39472 		/* Read-only file */
39473 		return -1;
39474 	}
39475 	/* File is writable */
39476 	return JX9_OK;
39477 }
39478 /* int (*xExecutable)(const char *) */
WinVfs_isexecutable(const char * zPath)39479 static int WinVfs_isexecutable(const char *zPath)
39480 {
39481 	void * pConverted;
39482 	DWORD dwAttr;
39483 	pConverted = jx9convertUtf8Filename(zPath);
39484 	if( pConverted == 0 ){
39485 		return -1;
39486 	}
39487 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39488 	HeapFree(GetProcessHeap(), 0, pConverted);
39489 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39490 		return -1;
39491 	}
39492 	if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
39493 		/* Not a regular file */
39494 		return -1;
39495 	}
39496 	/* File is executable */
39497 	return JX9_OK;
39498 }
39499 /* int (*xFiletype)(const char *, jx9_context *) */
WinVfs_Filetype(const char * zPath,jx9_context * pCtx)39500 static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
39501 {
39502 	void * pConverted;
39503 	DWORD dwAttr;
39504 	pConverted = jx9convertUtf8Filename(zPath);
39505 	if( pConverted == 0 ){
39506 		/* Expand 'unknown' */
39507 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39508 		return -1;
39509 	}
39510 	dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
39511 	HeapFree(GetProcessHeap(), 0, pConverted);
39512 	if( dwAttr == INVALID_FILE_ATTRIBUTES ){
39513 		/* Expand 'unknown' */
39514 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39515 		return -1;
39516 	}
39517 	if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
39518 		jx9_result_string(pCtx, "file", sizeof("file")-1);
39519 	}else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
39520 		jx9_result_string(pCtx, "dir", sizeof("dir")-1);
39521 	}else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
39522 		jx9_result_string(pCtx, "link", sizeof("link")-1);
39523 	}else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
39524 		jx9_result_string(pCtx, "block", sizeof("block")-1);
39525 	}else{
39526 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
39527 	}
39528 	return JX9_OK;
39529 }
39530 /* int (*xGetenv)(const char *, jx9_context *) */
WinVfs_Getenv(const char * zVar,jx9_context * pCtx)39531 static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
39532 {
39533 	char zValue[1024];
39534 	DWORD n;
39535 	/*
39536 	 * According to MSDN
39537 	 * If lpBuffer is not large enough to hold the data, the return
39538 	 * value is the buffer size, in characters, required to hold the
39539 	 * string and its terminating null character and the contents
39540 	 * of lpBuffer are undefined.
39541 	 */
39542 	n = sizeof(zValue);
39543 	SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
39544 	/* Extract the environment value */
39545 	n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
39546 	if( !n ){
39547 		/* No such variable*/
39548 		return -1;
39549 	}
39550 	jx9_result_string(pCtx, zValue, (int)n);
39551 	return JX9_OK;
39552 }
39553 /* int (*xSetenv)(const char *, const char *) */
WinVfs_Setenv(const char * zName,const char * zValue)39554 static int WinVfs_Setenv(const char *zName, const char *zValue)
39555 {
39556 	BOOL rc;
39557 	rc = SetEnvironmentVariableA(zName, zValue);
39558 	return rc ? JX9_OK : -1;
39559 }
39560 /* int (*xMmap)(const char *, void **, jx9_int64 *) */
WinVfs_Mmap(const char * zPath,void ** ppMap,jx9_int64 * pSize)39561 static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
39562 {
39563 	DWORD dwSizeLow, dwSizeHigh;
39564 	HANDLE pHandle, pMapHandle;
39565 	void *pConverted, *pView;
39566 
39567 	pConverted = jx9convertUtf8Filename(zPath);
39568 	if( pConverted == 0 ){
39569 		return -1;
39570 	}
39571 	pHandle = OpenReadOnly((LPCWSTR)pConverted);
39572 	HeapFree(GetProcessHeap(), 0, pConverted);
39573 	if( pHandle == 0 ){
39574 		return -1;
39575 	}
39576 	/* Get the file size */
39577 	dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
39578 	/* Create the mapping */
39579 	pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
39580 	if( pMapHandle == 0 ){
39581 		CloseHandle(pHandle);
39582 		return -1;
39583 	}
39584 	*pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
39585 	/* Obtain the view */
39586 	pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
39587 	if( pView ){
39588 		/* Let the upper layer point to the view */
39589 		*ppMap = pView;
39590 	}
39591 	/* Close the handle
39592 	 * According to MSDN it's OK the close the HANDLES.
39593 	 */
39594 	CloseHandle(pMapHandle);
39595 	CloseHandle(pHandle);
39596 	return pView ? JX9_OK : -1;
39597 }
39598 /* void (*xUnmap)(void *, jx9_int64)  */
WinVfs_Unmap(void * pView,jx9_int64 nSize)39599 static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
39600 {
39601 	nSize = 0; /* Compiler warning */
39602 	UnmapViewOfFile(pView);
39603 }
39604 /* void (*xTempDir)(jx9_context *) */
WinVfs_TempDir(jx9_context * pCtx)39605 static void WinVfs_TempDir(jx9_context *pCtx)
39606 {
39607 	CHAR zTemp[1024];
39608 	DWORD n;
39609 	n = GetTempPathA(sizeof(zTemp), zTemp);
39610 	if( n < 1 ){
39611 		/* Assume the default windows temp directory */
39612 		jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
39613 	}else{
39614 		jx9_result_string(pCtx, zTemp, (int)n);
39615 	}
39616 }
39617 /* unsigned int (*xProcessId)(void) */
WinVfs_ProcessId(void)39618 static unsigned int WinVfs_ProcessId(void)
39619 {
39620 	DWORD nID = 0;
39621 #ifndef __MINGW32__
39622 	nID = GetProcessId(GetCurrentProcess());
39623 #endif /* __MINGW32__ */
39624 	return (unsigned int)nID;
39625 }
39626 
39627 /* Export the windows vfs */
39628 static const jx9_vfs sWinVfs = {
39629 	"Windows_vfs",
39630 	JX9_VFS_VERSION,
39631 	WinVfs_chdir,    /* int (*xChdir)(const char *) */
39632 	0,               /* int (*xChroot)(const char *); */
39633 	WinVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
39634 	WinVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
39635 	WinVfs_rmdir,    /* int (*xRmdir)(const char *) */
39636 	WinVfs_isdir,    /* int (*xIsdir)(const char *) */
39637 	WinVfs_Rename,   /* int (*xRename)(const char *, const char *) */
39638 	WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
39639 	WinVfs_Sleep,               /* int (*xSleep)(unsigned int) */
39640 	WinVfs_unlink,   /* int (*xUnlink)(const char *) */
39641 	WinVfs_FileExists, /* int (*xFileExists)(const char *) */
39642 	0, /*int (*xChmod)(const char *, int)*/
39643 	0, /*int (*xChown)(const char *, const char *)*/
39644 	0, /*int (*xChgrp)(const char *, const char *)*/
39645 	WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
39646 	WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
39647 	WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
39648 	WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
39649 	WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
39650 	WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
39651 	WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
39652 	WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
39653 	WinVfs_isfile,     /* int (*xIsfile)(const char *) */
39654 	WinVfs_islink,     /* int (*xIslink)(const char *) */
39655 	WinVfs_isfile,     /* int (*xReadable)(const char *) */
39656 	WinVfs_iswritable, /* int (*xWritable)(const char *) */
39657 	WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
39658 	WinVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
39659 	WinVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
39660 	WinVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */
39661 	WinVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
39662 	WinVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
39663 	WinVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
39664 	0,                 /* int (*xLink)(const char *, const char *, int) */
39665 	0,                 /* int (*xUmask)(int) */
39666 	WinVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
39667 	WinVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
39668 	0, /* int (*xUid)(void) */
39669 	0, /* int (*xGid)(void) */
39670 	0, /* void (*xUsername)(jx9_context *) */
39671 	0  /* int (*xExec)(const char *, jx9_context *) */
39672 };
39673 /* Windows file IO */
39674 #ifndef INVALID_SET_FILE_POINTER
39675 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
39676 #endif
39677 /* int (*xOpen)(const char *, int, jx9_value *, void **) */
WinFile_Open(const char * zPath,int iOpenMode,jx9_value * pResource,void ** ppHandle)39678 static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
39679 {
39680 	DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
39681 	DWORD dwAccess = GENERIC_READ;
39682 	DWORD dwShare, dwCreate;
39683 	void *pConverted;
39684 	HANDLE pHandle;
39685 
39686 	pConverted = jx9convertUtf8Filename(zPath);
39687 	if( pConverted == 0 ){
39688 		return -1;
39689 	}
39690 	/* Set the desired flags according to the open mode */
39691 	if( iOpenMode & JX9_IO_OPEN_CREATE ){
39692 		/* Open existing file, or create if it doesn't exist */
39693 		dwCreate = OPEN_ALWAYS;
39694 		if( iOpenMode & JX9_IO_OPEN_TRUNC ){
39695 			/* If the specified file exists and is writable, the function overwrites the file */
39696 			dwCreate = CREATE_ALWAYS;
39697 		}
39698 	}else if( iOpenMode & JX9_IO_OPEN_EXCL ){
39699 		/* Creates a new file, only if it does not already exist.
39700 		* If the file exists, it fails.
39701 		*/
39702 		dwCreate = CREATE_NEW;
39703 	}else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
39704 		/* Opens a file and truncates it so that its size is zero bytes
39705 		 * The file must exist.
39706 		 */
39707 		dwCreate = TRUNCATE_EXISTING;
39708 	}else{
39709 		/* Opens a file, only if it exists. */
39710 		dwCreate = OPEN_EXISTING;
39711 	}
39712 	if( iOpenMode & JX9_IO_OPEN_RDWR ){
39713 		/* Read+Write access */
39714 		dwAccess |= GENERIC_WRITE;
39715 	}else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
39716 		/* Write only access */
39717 		dwAccess = GENERIC_WRITE;
39718 	}
39719 	if( iOpenMode & JX9_IO_OPEN_APPEND ){
39720 		/* Append mode */
39721 		dwAccess = FILE_APPEND_DATA;
39722 	}
39723 	if( iOpenMode & JX9_IO_OPEN_TEMP ){
39724 		/* File is temporary */
39725 		dwType = FILE_ATTRIBUTE_TEMPORARY;
39726 	}
39727 	dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
39728 	pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
39729 	HeapFree(GetProcessHeap(), 0, pConverted);
39730 	if( pHandle == INVALID_HANDLE_VALUE){
39731 		SXUNUSED(pResource); /* MSVC warning */
39732 		return -1;
39733 	}
39734 	/* Make the handle accessible to the upper layer */
39735 	*ppHandle = (void *)pHandle;
39736 	return JX9_OK;
39737 }
39738 /* An instance of the following structure is used to record state information
39739  * while iterating throw directory entries.
39740  */
39741 typedef struct WinDir_Info WinDir_Info;
39742 struct WinDir_Info
39743 {
39744 	HANDLE pDirHandle;
39745 	void *pPath;
39746 	WIN32_FIND_DATAW sInfo;
39747 	int rc;
39748 };
39749 /* int (*xOpenDir)(const char *, jx9_value *, void **) */
WinDir_Open(const char * zPath,jx9_value * pResource,void ** ppHandle)39750 static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
39751 {
39752 	WinDir_Info *pDirInfo;
39753 	void *pConverted;
39754 	char *zPrep;
39755 	sxu32 n;
39756 	/* Prepare the path */
39757 	n = SyStrlen(zPath);
39758 	zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
39759 	if( zPrep == 0 ){
39760 		return -1;
39761 	}
39762 	SyMemcpy((const void *)zPath, zPrep, n);
39763 	zPrep[n]   = '\\';
39764 	zPrep[n+1] =  '*';
39765 	zPrep[n+2] = 0;
39766 	pConverted = jx9convertUtf8Filename(zPrep);
39767 	HeapFree(GetProcessHeap(), 0, zPrep);
39768 	if( pConverted == 0 ){
39769 		return -1;
39770 	}
39771 	/* Allocate a new instance */
39772 	pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
39773 	if( pDirInfo == 0 ){
39774 		pResource = 0; /* Compiler warning */
39775 		return -1;
39776 	}
39777 	pDirInfo->rc = SXRET_OK;
39778 	pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
39779 	if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
39780 		/* Cannot open directory */
39781 		HeapFree(GetProcessHeap(), 0, pConverted);
39782 		HeapFree(GetProcessHeap(), 0, pDirInfo);
39783 		return -1;
39784 	}
39785 	/* Save the path */
39786 	pDirInfo->pPath = pConverted;
39787 	/* Save our structure */
39788 	*ppHandle = pDirInfo;
39789 	return JX9_OK;
39790 }
39791 /* void (*xCloseDir)(void *) */
WinDir_Close(void * pUserData)39792 static void WinDir_Close(void *pUserData)
39793 {
39794 	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39795 	if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
39796 		FindClose(pDirInfo->pDirHandle);
39797 	}
39798 	HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
39799 	HeapFree(GetProcessHeap(), 0, pDirInfo);
39800 }
39801 /* void (*xClose)(void *); */
WinFile_Close(void * pUserData)39802 static void WinFile_Close(void *pUserData)
39803 {
39804 	HANDLE pHandle = (HANDLE)pUserData;
39805 	CloseHandle(pHandle);
39806 }
39807 /* int (*xReadDir)(void *, jx9_context *) */
WinDir_Read(void * pUserData,jx9_context * pCtx)39808 static int WinDir_Read(void *pUserData, jx9_context *pCtx)
39809 {
39810 	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39811 	LPWIN32_FIND_DATAW pData;
39812 	char *zName;
39813 	BOOL rc;
39814 	sxu32 n;
39815 	if( pDirInfo->rc != SXRET_OK ){
39816 		/* No more entry to process */
39817 		return -1;
39818 	}
39819 	pData = &pDirInfo->sInfo;
39820 	for(;;){
39821 		zName = jx9unicodeToUtf8(pData->cFileName);
39822 		if( zName == 0 ){
39823 			/* Out of memory */
39824 			return -1;
39825 		}
39826 		n = SyStrlen(zName);
39827 		/* Ignore '.' && '..' */
39828 		if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
39829 			break;
39830 		}
39831 		HeapFree(GetProcessHeap(), 0, zName);
39832 		rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
39833 		if( !rc ){
39834 			return -1;
39835 		}
39836 	}
39837 	/* Return the current file name */
39838 	jx9_result_string(pCtx, zName, -1);
39839 	HeapFree(GetProcessHeap(), 0, zName);
39840 	/* Point to the next entry */
39841 	rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
39842 	if( !rc ){
39843 		pDirInfo->rc = SXERR_EOF;
39844 	}
39845 	return JX9_OK;
39846 }
39847 /* void (*xRewindDir)(void *) */
WinDir_RewindDir(void * pUserData)39848 static void WinDir_RewindDir(void *pUserData)
39849 {
39850 	WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
39851 	FindClose(pDirInfo->pDirHandle);
39852 	pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
39853 	if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
39854 		pDirInfo->rc = SXERR_EOF;
39855 	}else{
39856 		pDirInfo->rc = SXRET_OK;
39857 	}
39858 }
39859 /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
WinFile_Read(void * pOS,void * pBuffer,jx9_int64 nDatatoRead)39860 static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
39861 {
39862 	HANDLE pHandle = (HANDLE)pOS;
39863 	DWORD nRd;
39864 	BOOL rc;
39865 	rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
39866 	if( !rc ){
39867 		/* EOF or IO error */
39868 		return -1;
39869 	}
39870 	return (jx9_int64)nRd;
39871 }
39872 /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
WinFile_Write(void * pOS,const void * pBuffer,jx9_int64 nWrite)39873 static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
39874 {
39875 	const char *zData = (const char *)pBuffer;
39876 	HANDLE pHandle = (HANDLE)pOS;
39877 	jx9_int64 nCount;
39878 	DWORD nWr;
39879 	BOOL rc;
39880 	nWr = 0;
39881 	nCount = 0;
39882 	for(;;){
39883 		if( nWrite < 1 ){
39884 			break;
39885 		}
39886 		rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
39887 		if( !rc ){
39888 			/* IO error */
39889 			break;
39890 		}
39891 		nWrite -= nWr;
39892 		nCount += nWr;
39893 		zData += nWr;
39894 	}
39895 	if( nWrite > 0 ){
39896 		return -1;
39897 	}
39898 	return nCount;
39899 }
39900 /* int (*xSeek)(void *, jx9_int64, int) */
WinFile_Seek(void * pUserData,jx9_int64 iOfft,int whence)39901 static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
39902 {
39903 	HANDLE pHandle = (HANDLE)pUserData;
39904 	DWORD dwMove, dwNew;
39905 	LONG nHighOfft;
39906 	switch(whence){
39907 	case 1:/*SEEK_CUR*/
39908 		dwMove = FILE_CURRENT;
39909 		break;
39910 	case 2: /* SEEK_END */
39911 		dwMove = FILE_END;
39912 		break;
39913 	case 0: /* SEEK_SET */
39914 	default:
39915 		dwMove = FILE_BEGIN;
39916 		break;
39917 	}
39918 	nHighOfft = (LONG)(iOfft >> 32);
39919 	dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
39920 	if( dwNew == INVALID_SET_FILE_POINTER ){
39921 		return -1;
39922 	}
39923 	return JX9_OK;
39924 }
39925 /* int (*xLock)(void *, int) */
WinFile_Lock(void * pUserData,int lock_type)39926 static int WinFile_Lock(void *pUserData, int lock_type)
39927 {
39928 	HANDLE pHandle = (HANDLE)pUserData;
39929 	static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
39930 	OVERLAPPED sDummy;
39931 	BOOL rc;
39932 	SyZero(&sDummy, sizeof(sDummy));
39933 	/* Get the file size */
39934 	if( lock_type < 1 ){
39935 		/* Unlock the file */
39936 		rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
39937 	}else{
39938 		DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
39939 		/* Lock the file */
39940 		if( lock_type == 1 /* LOCK_EXCL */ ){
39941 			dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
39942 		}
39943 		dwLo = GetFileSize(pHandle, &dwHi);
39944 		rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
39945 	}
39946 	return rc ? JX9_OK : -1 /* Lock error */;
39947 }
39948 /* jx9_int64 (*xTell)(void *) */
WinFile_Tell(void * pUserData)39949 static jx9_int64 WinFile_Tell(void *pUserData)
39950 {
39951 	HANDLE pHandle = (HANDLE)pUserData;
39952 	DWORD dwNew;
39953 	dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
39954 	if( dwNew == INVALID_SET_FILE_POINTER ){
39955 		return -1;
39956 	}
39957 	return (jx9_int64)dwNew;
39958 }
39959 /* int (*xTrunc)(void *, jx9_int64) */
WinFile_Trunc(void * pUserData,jx9_int64 nOfft)39960 static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
39961 {
39962 	HANDLE pHandle = (HANDLE)pUserData;
39963 	LONG HighOfft;
39964 	DWORD dwNew;
39965 	BOOL rc;
39966 	HighOfft = (LONG)(nOfft >> 32);
39967 	dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
39968 	if( dwNew == INVALID_SET_FILE_POINTER ){
39969 		return -1;
39970 	}
39971 	rc = SetEndOfFile(pHandle);
39972 	return rc ? JX9_OK : -1;
39973 }
39974 /* int (*xSync)(void *); */
WinFile_Sync(void * pUserData)39975 static int WinFile_Sync(void *pUserData)
39976 {
39977 	HANDLE pHandle = (HANDLE)pUserData;
39978 	BOOL rc;
39979 	rc = FlushFileBuffers(pHandle);
39980 	return rc ? JX9_OK : - 1;
39981 }
39982 /* int (*xStat)(void *, jx9_value *, jx9_value *) */
WinFile_Stat(void * pUserData,jx9_value * pArray,jx9_value * pWorker)39983 static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
39984 {
39985 	BY_HANDLE_FILE_INFORMATION sInfo;
39986 	HANDLE pHandle = (HANDLE)pUserData;
39987 	BOOL rc;
39988 	rc = GetFileInformationByHandle(pHandle, &sInfo);
39989 	if( !rc ){
39990 		return -1;
39991 	}
39992 	/* dev */
39993 	jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
39994 	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
39995 	/* ino */
39996 	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
39997 	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
39998 	/* mode */
39999 	jx9_value_int(pWorker, 0);
40000 	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40001 	/* nlink */
40002 	jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
40003 	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40004 	/* uid, gid, rdev */
40005 	jx9_value_int(pWorker, 0);
40006 	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40007 	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40008 	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40009 	/* size */
40010 	jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
40011 	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40012 	/* atime */
40013 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
40014 	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40015 	/* mtime */
40016 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
40017 	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40018 	/* ctime */
40019 	jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
40020 	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40021 	/* blksize, blocks */
40022 	jx9_value_int(pWorker, 0);
40023 	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40024 	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40025 	return JX9_OK;
40026 }
40027 /* Export the file:// stream */
40028 static const jx9_io_stream sWinFileStream = {
40029 	"file", /* Stream name */
40030 	JX9_IO_STREAM_VERSION,
40031 	WinFile_Open,  /* xOpen */
40032 	WinDir_Open,   /* xOpenDir */
40033 	WinFile_Close, /* xClose */
40034 	WinDir_Close,  /* xCloseDir */
40035 	WinFile_Read,  /* xRead */
40036 	WinDir_Read,   /* xReadDir */
40037 	WinFile_Write, /* xWrite */
40038 	WinFile_Seek,  /* xSeek */
40039 	WinFile_Lock,  /* xLock */
40040 	WinDir_RewindDir, /* xRewindDir */
40041 	WinFile_Tell,  /* xTell */
40042 	WinFile_Trunc, /* xTrunc */
40043 	WinFile_Sync,  /* xSeek */
40044 	WinFile_Stat   /* xStat */
40045 };
40046 #elif defined(__UNIXES__)
40047 /*
40048  * UNIX VFS implementation for the JX9 engine.
40049  * Authors:
40050  *    Symisc Systems, devel@symisc.net.
40051  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
40052  * Status:
40053  *    Stable.
40054  */
40055 #include <sys/types.h>
40056 #include <limits.h>
40057 #include <fcntl.h>
40058 #include <unistd.h>
40059 #include <sys/uio.h>
40060 #include <sys/stat.h>
40061 #include <sys/mman.h>
40062 #include <sys/file.h>
40063 #include <pwd.h>
40064 #include <grp.h>
40065 #include <dirent.h>
40066 #include <utime.h>
40067 #include <stdio.h>
40068 #include <stdlib.h>
40069 /* int (*xchdir)(const char *) */
UnixVfs_chdir(const char * zPath)40070 static int UnixVfs_chdir(const char *zPath)
40071 {
40072   int rc;
40073   rc = chdir(zPath);
40074   return rc == 0 ? JX9_OK : -1;
40075 }
40076 /* int (*xGetcwd)(jx9_context *) */
UnixVfs_getcwd(jx9_context * pCtx)40077 static int UnixVfs_getcwd(jx9_context *pCtx)
40078 {
40079 	char zBuf[4096];
40080 	char *zDir;
40081 	/* Get the current directory */
40082 	zDir = getcwd(zBuf, sizeof(zBuf));
40083 	if( zDir == 0 ){
40084 	  return -1;
40085     }
40086 	jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
40087 	return JX9_OK;
40088 }
40089 /* int (*xMkdir)(const char *, int, int) */
UnixVfs_mkdir(const char * zPath,int mode,int recursive)40090 static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
40091 {
40092 	int rc;
40093         rc = mkdir(zPath, mode);
40094 	recursive = 0; /* cc warning */
40095 	return rc == 0 ? JX9_OK : -1;
40096 }
40097 /* int (*xRmdir)(const char *) */
UnixVfs_rmdir(const char * zPath)40098 static int UnixVfs_rmdir(const char *zPath)
40099 {
40100 	int rc;
40101 	rc = rmdir(zPath);
40102 	return rc == 0 ? JX9_OK : -1;
40103 }
40104 /* int (*xIsdir)(const char *) */
UnixVfs_isdir(const char * zPath)40105 static int UnixVfs_isdir(const char *zPath)
40106 {
40107 	struct stat st;
40108 	int rc;
40109 	rc = stat(zPath, &st);
40110 	if( rc != 0 ){
40111 	 return -1;
40112 	}
40113 	rc = S_ISDIR(st.st_mode);
40114 	return rc ? JX9_OK : -1 ;
40115 }
40116 /* int (*xRename)(const char *, const char *) */
UnixVfs_Rename(const char * zOld,const char * zNew)40117 static int UnixVfs_Rename(const char *zOld, const char *zNew)
40118 {
40119 	int rc;
40120 	rc = rename(zOld, zNew);
40121 	return rc == 0 ? JX9_OK : -1;
40122 }
40123 /* int (*xRealpath)(const char *, jx9_context *) */
UnixVfs_Realpath(const char * zPath,jx9_context * pCtx)40124 static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
40125 {
40126 #ifndef JX9_UNIX_OLD_LIBC
40127 	char *zReal;
40128 	zReal = realpath(zPath, 0);
40129 	if( zReal == 0 ){
40130 	  return -1;
40131 	}
40132 	jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
40133         /* Release the allocated buffer */
40134 	free(zReal);
40135 	return JX9_OK;
40136 #else
40137     zPath = 0; /* cc warning */
40138     pCtx = 0;
40139     return -1;
40140 #endif
40141 }
40142 /* int (*xSleep)(unsigned int) */
UnixVfs_Sleep(unsigned int uSec)40143 static int UnixVfs_Sleep(unsigned int uSec)
40144 {
40145 	usleep(uSec);
40146 	return JX9_OK;
40147 }
40148 /* int (*xUnlink)(const char *) */
UnixVfs_unlink(const char * zPath)40149 static int UnixVfs_unlink(const char *zPath)
40150 {
40151 	int rc;
40152 	rc = unlink(zPath);
40153 	return rc == 0 ? JX9_OK : -1 ;
40154 }
40155 /* int (*xFileExists)(const char *) */
UnixVfs_FileExists(const char * zPath)40156 static int UnixVfs_FileExists(const char *zPath)
40157 {
40158 	int rc;
40159 	rc = access(zPath, F_OK);
40160 	return rc == 0 ? JX9_OK : -1;
40161 }
40162 /* jx9_int64 (*xFileSize)(const char *) */
UnixVfs_FileSize(const char * zPath)40163 static jx9_int64 UnixVfs_FileSize(const char *zPath)
40164 {
40165 	struct stat st;
40166 	int rc;
40167 	rc = stat(zPath, &st);
40168 	if( rc != 0 ){
40169 	 return -1;
40170 	}
40171 	return (jx9_int64)st.st_size;
40172 }
40173 /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
UnixVfs_Touch(const char * zPath,jx9_int64 touch_time,jx9_int64 access_time)40174 static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
40175 {
40176 	struct utimbuf ut;
40177 	int rc;
40178 	ut.actime  = (time_t)access_time;
40179 	ut.modtime = (time_t)touch_time;
40180 	rc = utime(zPath, &ut);
40181 	if( rc != 0 ){
40182 	 return -1;
40183 	}
40184 	return JX9_OK;
40185 }
40186 /* jx9_int64 (*xFileAtime)(const char *) */
UnixVfs_FileAtime(const char * zPath)40187 static jx9_int64 UnixVfs_FileAtime(const char *zPath)
40188 {
40189 	struct stat st;
40190 	int rc;
40191 	rc = stat(zPath, &st);
40192 	if( rc != 0 ){
40193 	 return -1;
40194 	}
40195 	return (jx9_int64)st.st_atime;
40196 }
40197 /* jx9_int64 (*xFileMtime)(const char *) */
UnixVfs_FileMtime(const char * zPath)40198 static jx9_int64 UnixVfs_FileMtime(const char *zPath)
40199 {
40200 	struct stat st;
40201 	int rc;
40202 	rc = stat(zPath, &st);
40203 	if( rc != 0 ){
40204 	 return -1;
40205 	}
40206 	return (jx9_int64)st.st_mtime;
40207 }
40208 /* jx9_int64 (*xFileCtime)(const char *) */
UnixVfs_FileCtime(const char * zPath)40209 static jx9_int64 UnixVfs_FileCtime(const char *zPath)
40210 {
40211 	struct stat st;
40212 	int rc;
40213 	rc = stat(zPath, &st);
40214 	if( rc != 0 ){
40215 	 return -1;
40216 	}
40217 	return (jx9_int64)st.st_ctime;
40218 }
40219 /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
UnixVfs_Stat(const char * zPath,jx9_value * pArray,jx9_value * pWorker)40220 static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
40221 {
40222 	struct stat st;
40223 	int rc;
40224 	rc = stat(zPath, &st);
40225 	if( rc != 0 ){
40226 	 return -1;
40227 	}
40228 	/* dev */
40229 	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40230 	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40231 	/* ino */
40232 	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40233 	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40234 	/* mode */
40235 	jx9_value_int(pWorker, (int)st.st_mode);
40236 	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40237 	/* nlink */
40238 	jx9_value_int(pWorker, (int)st.st_nlink);
40239 	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40240 	/* uid, gid, rdev */
40241 	jx9_value_int(pWorker, (int)st.st_uid);
40242 	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40243 	jx9_value_int(pWorker, (int)st.st_gid);
40244 	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40245 	jx9_value_int(pWorker, (int)st.st_rdev);
40246 	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40247 	/* size */
40248 	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40249 	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40250 	/* atime */
40251 	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40252 	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40253 	/* mtime */
40254 	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40255 	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40256 	/* ctime */
40257 	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40258 	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40259 	/* blksize, blocks */
40260 	jx9_value_int(pWorker, (int)st.st_blksize);
40261 	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40262 	jx9_value_int(pWorker, (int)st.st_blocks);
40263 	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40264 	return JX9_OK;
40265 }
40266 /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
UnixVfs_lStat(const char * zPath,jx9_value * pArray,jx9_value * pWorker)40267 static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
40268 {
40269 	struct stat st;
40270 	int rc;
40271 	rc = lstat(zPath, &st);
40272 	if( rc != 0 ){
40273 	 return -1;
40274 	}
40275 	/* dev */
40276 	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40277 	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40278 	/* ino */
40279 	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40280 	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40281 	/* mode */
40282 	jx9_value_int(pWorker, (int)st.st_mode);
40283 	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40284 	/* nlink */
40285 	jx9_value_int(pWorker, (int)st.st_nlink);
40286 	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40287 	/* uid, gid, rdev */
40288 	jx9_value_int(pWorker, (int)st.st_uid);
40289 	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40290 	jx9_value_int(pWorker, (int)st.st_gid);
40291 	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40292 	jx9_value_int(pWorker, (int)st.st_rdev);
40293 	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40294 	/* size */
40295 	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40296 	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40297 	/* atime */
40298 	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40299 	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40300 	/* mtime */
40301 	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40302 	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40303 	/* ctime */
40304 	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40305 	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40306 	/* blksize, blocks */
40307 	jx9_value_int(pWorker, (int)st.st_blksize);
40308 	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40309 	jx9_value_int(pWorker, (int)st.st_blocks);
40310 	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40311 	return JX9_OK;
40312 }
40313 /* int (*xChmod)(const char *, int) */
UnixVfs_Chmod(const char * zPath,int mode)40314 static int UnixVfs_Chmod(const char *zPath, int mode)
40315 {
40316     int rc;
40317     rc = chmod(zPath, (mode_t)mode);
40318     return rc == 0 ? JX9_OK : - 1;
40319 }
40320 /* int (*xChown)(const char *, const char *) */
UnixVfs_Chown(const char * zPath,const char * zUser)40321 static int UnixVfs_Chown(const char *zPath, const char *zUser)
40322 {
40323 #ifndef JX9_UNIX_STATIC_BUILD
40324   struct passwd *pwd;
40325   uid_t uid;
40326   int rc;
40327   pwd = getpwnam(zUser);   /* Try getting UID for username */
40328   if (pwd == 0) {
40329     return -1;
40330   }
40331   uid = pwd->pw_uid;
40332   rc = chown(zPath, uid, -1);
40333   return rc == 0 ? JX9_OK : -1;
40334 #else
40335 	SXUNUSED(zPath);
40336 	SXUNUSED(zUser);
40337 	return -1;
40338 #endif /* JX9_UNIX_STATIC_BUILD */
40339 }
40340 /* int (*xChgrp)(const char *, const char *) */
UnixVfs_Chgrp(const char * zPath,const char * zGroup)40341 static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
40342 {
40343 #ifndef JX9_UNIX_STATIC_BUILD
40344   struct group *group;
40345   gid_t gid;
40346   int rc;
40347   group = getgrnam(zGroup);
40348   if (group == 0) {
40349     return -1;
40350   }
40351   gid = group->gr_gid;
40352   rc = chown(zPath, -1, gid);
40353   return rc == 0 ? JX9_OK : -1;
40354 #else
40355 	SXUNUSED(zPath);
40356 	SXUNUSED(zGroup);
40357 	return -1;
40358 #endif /* JX9_UNIX_STATIC_BUILD */
40359 }
40360 /* int (*xIsfile)(const char *) */
UnixVfs_isfile(const char * zPath)40361 static int UnixVfs_isfile(const char *zPath)
40362 {
40363 	struct stat st;
40364 	int rc;
40365 	rc = stat(zPath, &st);
40366 	if( rc != 0 ){
40367 	 return -1;
40368 	}
40369 	rc = S_ISREG(st.st_mode);
40370 	return rc ? JX9_OK : -1 ;
40371 }
40372 /* int (*xIslink)(const char *) */
UnixVfs_islink(const char * zPath)40373 static int UnixVfs_islink(const char *zPath)
40374 {
40375 	struct stat st;
40376 	int rc;
40377 	rc = stat(zPath, &st);
40378 	if( rc != 0 ){
40379 	 return -1;
40380 	}
40381 	rc = S_ISLNK(st.st_mode);
40382 	return rc ? JX9_OK : -1 ;
40383 }
40384 /* int (*xReadable)(const char *) */
UnixVfs_isreadable(const char * zPath)40385 static int UnixVfs_isreadable(const char *zPath)
40386 {
40387 	int rc;
40388 	rc = access(zPath, R_OK);
40389 	return rc == 0 ? JX9_OK : -1;
40390 }
40391 /* int (*xWritable)(const char *) */
UnixVfs_iswritable(const char * zPath)40392 static int UnixVfs_iswritable(const char *zPath)
40393 {
40394 	int rc;
40395 	rc = access(zPath, W_OK);
40396 	return rc == 0 ? JX9_OK : -1;
40397 }
40398 /* int (*xExecutable)(const char *) */
UnixVfs_isexecutable(const char * zPath)40399 static int UnixVfs_isexecutable(const char *zPath)
40400 {
40401 	int rc;
40402 	rc = access(zPath, X_OK);
40403 	return rc == 0 ? JX9_OK : -1;
40404 }
40405 /* int (*xFiletype)(const char *, jx9_context *) */
UnixVfs_Filetype(const char * zPath,jx9_context * pCtx)40406 static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
40407 {
40408 	struct stat st;
40409 	int rc;
40410     rc = stat(zPath, &st);
40411 	if( rc != 0 ){
40412 	  /* Expand 'unknown' */
40413 	  jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
40414 	  return -1;
40415 	}
40416 	if(S_ISREG(st.st_mode) ){
40417 		jx9_result_string(pCtx, "file", sizeof("file")-1);
40418 	}else if(S_ISDIR(st.st_mode)){
40419 		jx9_result_string(pCtx, "dir", sizeof("dir")-1);
40420 	}else if(S_ISLNK(st.st_mode)){
40421 		jx9_result_string(pCtx, "link", sizeof("link")-1);
40422 	}else if(S_ISBLK(st.st_mode)){
40423 		jx9_result_string(pCtx, "block", sizeof("block")-1);
40424     }else if(S_ISSOCK(st.st_mode)){
40425 		jx9_result_string(pCtx, "socket", sizeof("socket")-1);
40426 	}else if(S_ISFIFO(st.st_mode)){
40427        jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
40428 	}else{
40429 		jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
40430 	}
40431 	return JX9_OK;
40432 }
40433 /* int (*xGetenv)(const char *, jx9_context *) */
UnixVfs_Getenv(const char * zVar,jx9_context * pCtx)40434 static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
40435 {
40436 	char *zEnv;
40437 	zEnv = getenv(zVar);
40438 	if( zEnv == 0 ){
40439 	  return -1;
40440 	}
40441 	jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
40442 	return JX9_OK;
40443 }
40444 /* int (*xSetenv)(const char *, const char *) */
UnixVfs_Setenv(const char * zName,const char * zValue)40445 static int UnixVfs_Setenv(const char *zName, const char *zValue)
40446 {
40447    int rc;
40448    rc = setenv(zName, zValue, 1);
40449    return rc == 0 ? JX9_OK : -1;
40450 }
40451 /* int (*xMmap)(const char *, void **, jx9_int64 *) */
UnixVfs_Mmap(const char * zPath,void ** ppMap,jx9_int64 * pSize)40452 static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
40453 {
40454 	struct stat st;
40455 	void *pMap;
40456 	int fd;
40457 	int rc;
40458 	/* Open the file in a read-only mode */
40459 	fd = open(zPath, O_RDONLY);
40460 	if( fd < 0 ){
40461 		return -1;
40462 	}
40463 	/* stat the handle */
40464 	fstat(fd, &st);
40465 	/* Obtain a memory view of the whole file */
40466 	pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
40467 	rc = JX9_OK;
40468 	if( pMap == MAP_FAILED ){
40469 		rc = -1;
40470 	}else{
40471 		/* Point to the memory view */
40472 		*ppMap = pMap;
40473 		*pSize = (jx9_int64)st.st_size;
40474 	}
40475 	close(fd);
40476 	return rc;
40477 }
40478 /* void (*xUnmap)(void *, jx9_int64)  */
UnixVfs_Unmap(void * pView,jx9_int64 nSize)40479 static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
40480 {
40481 	munmap(pView, (size_t)nSize);
40482 }
40483 /* void (*xTempDir)(jx9_context *) */
UnixVfs_TempDir(jx9_context * pCtx)40484 static void UnixVfs_TempDir(jx9_context *pCtx)
40485 {
40486 	static const char *azDirs[] = {
40487      "/var/tmp",
40488      "/usr/tmp",
40489 	 "/usr/local/tmp"
40490   };
40491   unsigned int i;
40492   struct stat buf;
40493   const char *zDir;
40494   zDir = getenv("TMPDIR");
40495   if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
40496 	  jx9_result_string(pCtx, zDir, -1);
40497 	  return;
40498   }
40499   for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
40500 	zDir=azDirs[i];
40501     if( zDir==0 ) continue;
40502     if( stat(zDir, &buf) ) continue;
40503     if( !S_ISDIR(buf.st_mode) ) continue;
40504     if( access(zDir, 07) ) continue;
40505     /* Got one */
40506 	jx9_result_string(pCtx, zDir, -1);
40507 	return;
40508   }
40509   /* Default temp dir */
40510   jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
40511 }
40512 /* unsigned int (*xProcessId)(void) */
UnixVfs_ProcessId(void)40513 static unsigned int UnixVfs_ProcessId(void)
40514 {
40515 	return (unsigned int)getpid();
40516 }
40517 /* int (*xUid)(void) */
UnixVfs_uid(void)40518 static int UnixVfs_uid(void)
40519 {
40520 	return (int)getuid();
40521 }
40522 /* int (*xGid)(void) */
UnixVfs_gid(void)40523 static int UnixVfs_gid(void)
40524 {
40525 	return (int)getgid();
40526 }
40527 /* int (*xUmask)(int) */
UnixVfs_Umask(int new_mask)40528 static int UnixVfs_Umask(int new_mask)
40529 {
40530 	int old_mask;
40531 	old_mask = umask(new_mask);
40532 	return old_mask;
40533 }
40534 /* void (*xUsername)(jx9_context *) */
UnixVfs_Username(jx9_context * pCtx)40535 static void UnixVfs_Username(jx9_context *pCtx)
40536 {
40537 #ifndef JX9_UNIX_STATIC_BUILD
40538   struct passwd *pwd;
40539   uid_t uid;
40540   uid = getuid();
40541   pwd = getpwuid(uid);   /* Try getting UID for username */
40542   if (pwd == 0) {
40543     return;
40544   }
40545   /* Return the username */
40546   jx9_result_string(pCtx, pwd->pw_name, -1);
40547 #else
40548   jx9_result_string(pCtx, "Unknown", -1);
40549 #endif /* JX9_UNIX_STATIC_BUILD */
40550   return;
40551 }
40552 /* int (*xLink)(const char *, const char *, int) */
UnixVfs_link(const char * zSrc,const char * zTarget,int is_sym)40553 static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
40554 {
40555 	int rc;
40556 	if( is_sym ){
40557 		/* Symbolic link */
40558 		rc = symlink(zSrc, zTarget);
40559 	}else{
40560 		/* Hard link */
40561 		rc = link(zSrc, zTarget);
40562 	}
40563 	return rc == 0 ? JX9_OK : -1;
40564 }
40565 /* int (*xChroot)(const char *) */
UnixVfs_chroot(const char * zRootDir)40566 static int UnixVfs_chroot(const char *zRootDir)
40567 {
40568 	int rc;
40569 	rc = chroot(zRootDir);
40570 	return rc == 0 ? JX9_OK : -1;
40571 }
40572 /* Export the UNIX vfs */
40573 static const jx9_vfs sUnixVfs = {
40574 	"Unix_vfs",
40575 	JX9_VFS_VERSION,
40576 	UnixVfs_chdir,    /* int (*xChdir)(const char *) */
40577 	UnixVfs_chroot,   /* int (*xChroot)(const char *); */
40578 	UnixVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
40579 	UnixVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
40580 	UnixVfs_rmdir,    /* int (*xRmdir)(const char *) */
40581 	UnixVfs_isdir,    /* int (*xIsdir)(const char *) */
40582 	UnixVfs_Rename,   /* int (*xRename)(const char *, const char *) */
40583 	UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
40584 	UnixVfs_Sleep,    /* int (*xSleep)(unsigned int) */
40585 	UnixVfs_unlink,   /* int (*xUnlink)(const char *) */
40586 	UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
40587 	UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
40588 	UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
40589 	UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
40590 	0,             /* jx9_int64 (*xFreeSpace)(const char *) */
40591 	0,             /* jx9_int64 (*xTotalSpace)(const char *) */
40592 	UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
40593 	UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
40594 	UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
40595 	UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
40596 	UnixVfs_Stat,  /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
40597 	UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
40598 	UnixVfs_isfile,     /* int (*xIsfile)(const char *) */
40599 	UnixVfs_islink,     /* int (*xIslink)(const char *) */
40600 	UnixVfs_isreadable, /* int (*xReadable)(const char *) */
40601 	UnixVfs_iswritable, /* int (*xWritable)(const char *) */
40602 	UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
40603 	UnixVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
40604 	UnixVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
40605 	UnixVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */
40606 	UnixVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
40607 	UnixVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
40608 	UnixVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
40609 	UnixVfs_link,       /* int (*xLink)(const char *, const char *, int) */
40610 	UnixVfs_Umask,      /* int (*xUmask)(int) */
40611 	UnixVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
40612 	UnixVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
40613 	UnixVfs_uid, /* int (*xUid)(void) */
40614 	UnixVfs_gid, /* int (*xGid)(void) */
40615 	UnixVfs_Username,    /* void (*xUsername)(jx9_context *) */
40616 	0 /* int (*xExec)(const char *, jx9_context *) */
40617 };
40618 /* UNIX File IO */
40619 #define JX9_UNIX_OPEN_MODE	0640 /* Default open mode */
40620 /* int (*xOpen)(const char *, int, jx9_value *, void **) */
UnixFile_Open(const char * zPath,int iOpenMode,jx9_value * pResource,void ** ppHandle)40621 static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
40622 {
40623 	int iOpen = O_RDONLY;
40624 	int fd;
40625 	/* Set the desired flags according to the open mode */
40626 	if( iOpenMode & JX9_IO_OPEN_CREATE ){
40627 		/* Open existing file, or create if it doesn't exist */
40628 		iOpen = O_CREAT;
40629 		if( iOpenMode & JX9_IO_OPEN_TRUNC ){
40630 			/* If the specified file exists and is writable, the function overwrites the file */
40631 			iOpen |= O_TRUNC;
40632 			SXUNUSED(pResource); /* cc warning */
40633 		}
40634 	}else if( iOpenMode & JX9_IO_OPEN_EXCL ){
40635 		/* Creates a new file, only if it does not already exist.
40636 		* If the file exists, it fails.
40637 		*/
40638 		iOpen = O_CREAT|O_EXCL;
40639 	}else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
40640 		/* Opens a file and truncates it so that its size is zero bytes
40641 		 * The file must exist.
40642 		 */
40643 		iOpen = O_RDWR|O_TRUNC;
40644 	}
40645 	if( iOpenMode & JX9_IO_OPEN_RDWR ){
40646 		/* Read+Write access */
40647 		iOpen &= ~O_RDONLY;
40648 		iOpen |= O_RDWR;
40649 	}else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
40650 		/* Write only access */
40651 		iOpen &= ~O_RDONLY;
40652 		iOpen |= O_WRONLY;
40653 	}
40654 	if( iOpenMode & JX9_IO_OPEN_APPEND ){
40655 		/* Append mode */
40656 		iOpen |= O_APPEND;
40657 	}
40658 #ifdef O_TEMP
40659 	if( iOpenMode & JX9_IO_OPEN_TEMP ){
40660 		/* File is temporary */
40661 		iOpen |= O_TEMP;
40662 	}
40663 #endif
40664 	/* Open the file now */
40665 	fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
40666 	if( fd < 0 ){
40667 		/* IO error */
40668 		return -1;
40669 	}
40670 	/* Save the handle */
40671 	*ppHandle = SX_INT_TO_PTR(fd);
40672 	return JX9_OK;
40673 }
40674 /* int (*xOpenDir)(const char *, jx9_value *, void **) */
UnixDir_Open(const char * zPath,jx9_value * pResource,void ** ppHandle)40675 static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
40676 {
40677 	DIR *pDir;
40678 	/* Open the target directory */
40679 	pDir = opendir(zPath);
40680 	if( pDir == 0 ){
40681 		pResource = 0; /* Compiler warning */
40682 		return -1;
40683 	}
40684 	/* Save our structure */
40685 	*ppHandle = pDir;
40686 	return JX9_OK;
40687 }
40688 /* void (*xCloseDir)(void *) */
UnixDir_Close(void * pUserData)40689 static void UnixDir_Close(void *pUserData)
40690 {
40691 	closedir((DIR *)pUserData);
40692 }
40693 /* void (*xClose)(void *); */
UnixFile_Close(void * pUserData)40694 static void UnixFile_Close(void *pUserData)
40695 {
40696 	close(SX_PTR_TO_INT(pUserData));
40697 }
40698 /* int (*xReadDir)(void *, jx9_context *) */
UnixDir_Read(void * pUserData,jx9_context * pCtx)40699 static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
40700 {
40701 	DIR *pDir = (DIR *)pUserData;
40702 	struct dirent *pEntry;
40703 	char *zName = 0; /* cc warning */
40704 	sxu32 n = 0;
40705 	for(;;){
40706 		pEntry = readdir(pDir);
40707 		if( pEntry == 0 ){
40708 			/* No more entries to process */
40709 			return -1;
40710 		}
40711 		zName = pEntry->d_name;
40712 		n = SyStrlen(zName);
40713 		/* Ignore '.' && '..' */
40714 		if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
40715 			break;
40716 		}
40717 		/* Next entry */
40718 	}
40719 	/* Return the current file name */
40720 	jx9_result_string(pCtx, zName, (int)n);
40721 	return JX9_OK;
40722 }
40723 /* void (*xRewindDir)(void *) */
UnixDir_Rewind(void * pUserData)40724 static void UnixDir_Rewind(void *pUserData)
40725 {
40726 	rewinddir((DIR *)pUserData);
40727 }
40728 /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
UnixFile_Read(void * pUserData,void * pBuffer,jx9_int64 nDatatoRead)40729 static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
40730 {
40731 	ssize_t nRd;
40732 	nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
40733 	if( nRd < 1 ){
40734 		/* EOF or IO error */
40735 		return -1;
40736 	}
40737 	return (jx9_int64)nRd;
40738 }
40739 /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
UnixFile_Write(void * pUserData,const void * pBuffer,jx9_int64 nWrite)40740 static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
40741 {
40742 	const char *zData = (const char *)pBuffer;
40743 	int fd = SX_PTR_TO_INT(pUserData);
40744 	jx9_int64 nCount;
40745 	ssize_t nWr;
40746 	nCount = 0;
40747 	for(;;){
40748 		if( nWrite < 1 ){
40749 			break;
40750 		}
40751 		nWr = write(fd, zData, (size_t)nWrite);
40752 		if( nWr < 1 ){
40753 			/* IO error */
40754 			break;
40755 		}
40756 		nWrite -= nWr;
40757 		nCount += nWr;
40758 		zData += nWr;
40759 	}
40760 	if( nWrite > 0 ){
40761 		return -1;
40762 	}
40763 	return nCount;
40764 }
40765 /* int (*xSeek)(void *, jx9_int64, int) */
UnixFile_Seek(void * pUserData,jx9_int64 iOfft,int whence)40766 static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
40767 {
40768 	off_t iNew;
40769 	switch(whence){
40770 	case 1:/*SEEK_CUR*/
40771 		whence = SEEK_CUR;
40772 		break;
40773 	case 2: /* SEEK_END */
40774 		whence = SEEK_END;
40775 		break;
40776 	case 0: /* SEEK_SET */
40777 	default:
40778 		whence = SEEK_SET;
40779 		break;
40780 	}
40781 	iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
40782 	if( iNew < 0 ){
40783 		return -1;
40784 	}
40785 	return JX9_OK;
40786 }
40787 /* int (*xLock)(void *, int) */
UnixFile_Lock(void * pUserData,int lock_type)40788 static int UnixFile_Lock(void *pUserData, int lock_type)
40789 {
40790 	int fd = SX_PTR_TO_INT(pUserData);
40791 	int rc = JX9_OK; /* cc warning */
40792 	if( lock_type < 0 ){
40793 		/* Unlock the file */
40794 		rc = flock(fd, LOCK_UN);
40795 	}else{
40796 		if( lock_type == 1 ){
40797 			/* Exculsive lock */
40798 			rc = flock(fd, LOCK_EX);
40799 		}else{
40800 			/* Shared lock */
40801 			rc = flock(fd, LOCK_SH);
40802 		}
40803 	}
40804 	return !rc ? JX9_OK : -1;
40805 }
40806 /* jx9_int64 (*xTell)(void *) */
UnixFile_Tell(void * pUserData)40807 static jx9_int64 UnixFile_Tell(void *pUserData)
40808 {
40809 	off_t iNew;
40810 	iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
40811 	return (jx9_int64)iNew;
40812 }
40813 /* int (*xTrunc)(void *, jx9_int64) */
UnixFile_Trunc(void * pUserData,jx9_int64 nOfft)40814 static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
40815 {
40816 	int rc;
40817 	rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
40818 	if( rc != 0 ){
40819 		return -1;
40820 	}
40821 	return JX9_OK;
40822 }
40823 /* int (*xSync)(void *); */
UnixFile_Sync(void * pUserData)40824 static int UnixFile_Sync(void *pUserData)
40825 {
40826 	int rc;
40827 	rc = fsync(SX_PTR_TO_INT(pUserData));
40828 	return rc == 0 ? JX9_OK : - 1;
40829 }
40830 /* int (*xStat)(void *, jx9_value *, jx9_value *) */
UnixFile_Stat(void * pUserData,jx9_value * pArray,jx9_value * pWorker)40831 static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
40832 {
40833 	struct stat st;
40834 	int rc;
40835 	rc = fstat(SX_PTR_TO_INT(pUserData), &st);
40836 	if( rc != 0 ){
40837 	 return -1;
40838 	}
40839 	/* dev */
40840 	jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
40841 	jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
40842 	/* ino */
40843 	jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
40844 	jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
40845 	/* mode */
40846 	jx9_value_int(pWorker, (int)st.st_mode);
40847 	jx9_array_add_strkey_elem(pArray, "mode", pWorker);
40848 	/* nlink */
40849 	jx9_value_int(pWorker, (int)st.st_nlink);
40850 	jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
40851 	/* uid, gid, rdev */
40852 	jx9_value_int(pWorker, (int)st.st_uid);
40853 	jx9_array_add_strkey_elem(pArray, "uid", pWorker);
40854 	jx9_value_int(pWorker, (int)st.st_gid);
40855 	jx9_array_add_strkey_elem(pArray, "gid", pWorker);
40856 	jx9_value_int(pWorker, (int)st.st_rdev);
40857 	jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
40858 	/* size */
40859 	jx9_value_int64(pWorker, (jx9_int64)st.st_size);
40860 	jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
40861 	/* atime */
40862 	jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
40863 	jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
40864 	/* mtime */
40865 	jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
40866 	jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
40867 	/* ctime */
40868 	jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
40869 	jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
40870 	/* blksize, blocks */
40871 	jx9_value_int(pWorker, (int)st.st_blksize);
40872 	jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
40873 	jx9_value_int(pWorker, (int)st.st_blocks);
40874 	jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
40875 	return JX9_OK;
40876 }
40877 /* Export the file:// stream */
40878 static const jx9_io_stream sUnixFileStream = {
40879 	"file", /* Stream name */
40880 	JX9_IO_STREAM_VERSION,
40881 	UnixFile_Open,  /* xOpen */
40882 	UnixDir_Open,   /* xOpenDir */
40883 	UnixFile_Close, /* xClose */
40884 	UnixDir_Close,  /* xCloseDir */
40885 	UnixFile_Read,  /* xRead */
40886 	UnixDir_Read,   /* xReadDir */
40887 	UnixFile_Write, /* xWrite */
40888 	UnixFile_Seek,  /* xSeek */
40889 	UnixFile_Lock,  /* xLock */
40890 	UnixDir_Rewind, /* xRewindDir */
40891 	UnixFile_Tell,  /* xTell */
40892 	UnixFile_Trunc, /* xTrunc */
40893 	UnixFile_Sync,  /* xSeek */
40894 	UnixFile_Stat   /* xStat */
40895 };
40896 #endif /* __WINNT__/__UNIXES__ */
40897 #endif /* JX9_DISABLE_DISK_IO */
40898 #endif /* JX9_DISABLE_BUILTIN_FUNC */
40899 /*
40900  * Export the builtin vfs.
40901  * Return a pointer to the builtin vfs if available.
40902  * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
40903  * Note:
40904  *  The built-in vfs is always available for Windows/UNIX systems.
40905  * Note:
40906  *  If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
40907  *  directives defined then this function return the null_vfs instead.
40908  */
jx9ExportBuiltinVfs(void)40909 JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
40910 {
40911 #ifndef JX9_DISABLE_BUILTIN_FUNC
40912 #ifdef JX9_DISABLE_DISK_IO
40913 	return &null_vfs;
40914 #else
40915 #ifdef __WINNT__
40916 	return &sWinVfs;
40917 #elif defined(__UNIXES__)
40918 	return &sUnixVfs;
40919 #else
40920 	return &null_vfs;
40921 #endif /* __WINNT__/__UNIXES__ */
40922 #endif /*JX9_DISABLE_DISK_IO*/
40923 #else
40924 	return &null_vfs;
40925 #endif /* JX9_DISABLE_BUILTIN_FUNC */
40926 }
40927 #ifndef JX9_DISABLE_BUILTIN_FUNC
40928 #ifndef JX9_DISABLE_DISK_IO
40929 /*
40930  * The following defines are mostly used by the UNIX built and have
40931  * no particular meaning on windows.
40932  */
40933 #ifndef STDIN_FILENO
40934 #define STDIN_FILENO	0
40935 #endif
40936 #ifndef STDOUT_FILENO
40937 #define STDOUT_FILENO	1
40938 #endif
40939 #ifndef STDERR_FILENO
40940 #define STDERR_FILENO	2
40941 #endif
40942 /*
40943  * jx9:// Accessing various I/O streams
40944  * According to the JX9 langage reference manual
40945  * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
40946  * and output streams, the standard input, output and error file descriptors.
40947  * jx9://stdin, jx9://stdout and jx9://stderr:
40948  *  Allow direct access to the corresponding input or output stream of the JX9 process.
40949  *  The stream references a duplicate file descriptor, so if you open jx9://stdin and later
40950  *  close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
40951  *  jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
40952  * jx9://output
40953  *  jx9://output is a write-only stream that allows you to write to the output buffer
40954  *  mechanism in the same way as print and print.
40955  */
40956 typedef struct jx9_stream_data jx9_stream_data;
40957 /* Supported IO streams */
40958 #define JX9_IO_STREAM_STDIN  1 /* jx9://stdin */
40959 #define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
40960 #define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
40961 #define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
40962  /* The following structure is the private data associated with the jx9:// stream */
40963 struct jx9_stream_data
40964 {
40965 	jx9_vm *pVm; /* VM that own this instance */
40966 	int iType;   /* Stream type */
40967 	union{
40968 		void *pHandle; /* Stream handle */
40969 		jx9_output_consumer sConsumer; /* VM output consumer */
40970 	}x;
40971 };
40972 /*
40973  * Allocate a new instance of the jx9_stream_data structure.
40974  */
JX9StreamDataInit(jx9_vm * pVm,int iType)40975 static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
40976 {
40977 	jx9_stream_data *pData;
40978 	if( pVm == 0 ){
40979 		return 0;
40980 	}
40981 	/* Allocate a new instance */
40982 	pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
40983 	if( pData == 0 ){
40984 		return 0;
40985 	}
40986 	/* Zero the structure */
40987 	SyZero(pData, sizeof(jx9_stream_data));
40988 	/* Initialize fields */
40989 	pData->iType = iType;
40990 	if( iType == JX9_IO_STREAM_OUTPUT ){
40991 		/* Point to the default VM consumer routine. */
40992 		pData->x.sConsumer = pVm->sVmConsumer;
40993 	}else{
40994 #ifdef __WINNT__
40995 		DWORD nChannel;
40996 		switch(iType){
40997 		case JX9_IO_STREAM_STDOUT:	nChannel = STD_OUTPUT_HANDLE; break;
40998 		case JX9_IO_STREAM_STDERR:  nChannel = STD_ERROR_HANDLE; break;
40999 		default:
41000 			nChannel = STD_INPUT_HANDLE;
41001 			break;
41002 		}
41003 		pData->x.pHandle = GetStdHandle(nChannel);
41004 #else
41005 		/* Assume an UNIX system */
41006 		int ifd = STDIN_FILENO;
41007 		switch(iType){
41008 		case JX9_IO_STREAM_STDOUT:  ifd = STDOUT_FILENO; break;
41009 		case JX9_IO_STREAM_STDERR:  ifd = STDERR_FILENO; break;
41010 		default:
41011 			break;
41012 		}
41013 		pData->x.pHandle = SX_INT_TO_PTR(ifd);
41014 #endif
41015 	}
41016 	pData->pVm = pVm;
41017 	return pData;
41018 }
41019 /*
41020  * Implementation of the jx9:// IO streams routines
41021  * Authors:
41022  *  Symisc Systems, devel@symisc.net.
41023  *  Copyright (C) Symisc Systems, http://jx9.symisc.net
41024  * Status:
41025  *   Stable.
41026  */
41027 /* int (*xOpen)(const char *, int, jx9_value *, void **) */
JX9StreamData_Open(const char * zName,int iMode,jx9_value * pResource,void ** ppHandle)41028 static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
41029 {
41030 	jx9_stream_data *pData;
41031 	SyString sStream;
41032 	SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
41033 	/* Trim leading and trailing white spaces */
41034 	SyStringFullTrim(&sStream);
41035 	/* Stream to open */
41036 	if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
41037 		iMode = JX9_IO_STREAM_STDIN;
41038 	}else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
41039 		iMode = JX9_IO_STREAM_OUTPUT;
41040 	}else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
41041 		iMode = JX9_IO_STREAM_STDOUT;
41042 	}else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
41043 		iMode = JX9_IO_STREAM_STDERR;
41044 	}else{
41045 		/* unknown stream name */
41046 		return -1;
41047 	}
41048 	/* Create our handle */
41049 	pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
41050 	if( pData == 0 ){
41051 		return -1;
41052 	}
41053 	/* Make the handle public */
41054 	*ppHandle = (void *)pData;
41055 	return JX9_OK;
41056 }
41057 /* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
JX9StreamData_Read(void * pHandle,void * pBuffer,jx9_int64 nDatatoRead)41058 static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
41059 {
41060 	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41061 	if( pData == 0 ){
41062 		return -1;
41063 	}
41064 	if( pData->iType != JX9_IO_STREAM_STDIN ){
41065 		/* Forbidden */
41066 		return -1;
41067 	}
41068 #ifdef __WINNT__
41069 	{
41070 		DWORD nRd;
41071 		BOOL rc;
41072 		rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
41073 		if( !rc ){
41074 			/* IO error */
41075 			return -1;
41076 		}
41077 		return (jx9_int64)nRd;
41078 	}
41079 #elif defined(__UNIXES__)
41080 	{
41081 		ssize_t nRd;
41082 		int fd;
41083 		fd = SX_PTR_TO_INT(pData->x.pHandle);
41084 		nRd = read(fd, pBuffer, (size_t)nDatatoRead);
41085 		if( nRd < 1 ){
41086 			return -1;
41087 		}
41088 		return (jx9_int64)nRd;
41089 	}
41090 #else
41091 	return -1;
41092 #endif
41093 }
41094 /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
JX9StreamData_Write(void * pHandle,const void * pBuf,jx9_int64 nWrite)41095 static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
41096 {
41097 	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41098 	if( pData == 0 ){
41099 		return -1;
41100 	}
41101 	if( pData->iType == JX9_IO_STREAM_STDIN ){
41102 		/* Forbidden */
41103 		return -1;
41104 	}else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
41105 		jx9_output_consumer *pCons = &pData->x.sConsumer;
41106 		int rc;
41107 		/* Call the vm output consumer */
41108 		rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
41109 		if( rc == JX9_ABORT ){
41110 			return -1;
41111 		}
41112 		return nWrite;
41113 	}
41114 #ifdef __WINNT__
41115 	{
41116 		DWORD nWr;
41117 		BOOL rc;
41118 		rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
41119 		if( !rc ){
41120 			/* IO error */
41121 			return -1;
41122 		}
41123 		return (jx9_int64)nWr;
41124 	}
41125 #elif defined(__UNIXES__)
41126 	{
41127 		ssize_t nWr;
41128 		int fd;
41129 		fd = SX_PTR_TO_INT(pData->x.pHandle);
41130 		nWr = write(fd, pBuf, (size_t)nWrite);
41131 		if( nWr < 1 ){
41132 			return -1;
41133 		}
41134 		return (jx9_int64)nWr;
41135 	}
41136 #else
41137 	return -1;
41138 #endif
41139 }
41140 /* void (*xClose)(void *) */
JX9StreamData_Close(void * pHandle)41141 static void JX9StreamData_Close(void *pHandle)
41142 {
41143 	jx9_stream_data *pData = (jx9_stream_data *)pHandle;
41144 	jx9_vm *pVm;
41145 	if( pData == 0 ){
41146 		return;
41147 	}
41148 	pVm = pData->pVm;
41149 	/* Free the instance */
41150 	SyMemBackendFree(&pVm->sAllocator, pData);
41151 }
41152 /* Export the jx9:// stream */
41153 static const jx9_io_stream sjx9Stream = {
41154 	"jx9",
41155 	JX9_IO_STREAM_VERSION,
41156 	JX9StreamData_Open,  /* xOpen */
41157 	0,   /* xOpenDir */
41158 	JX9StreamData_Close, /* xClose */
41159 	0,  /* xCloseDir */
41160 	JX9StreamData_Read,  /* xRead */
41161 	0,  /* xReadDir */
41162 	JX9StreamData_Write, /* xWrite */
41163 	0,  /* xSeek */
41164 	0,  /* xLock */
41165 	0,  /* xRewindDir */
41166 	0,  /* xTell */
41167 	0,  /* xTrunc */
41168 	0,  /* xSeek */
41169 	0   /* xStat */
41170 };
41171 #endif /* JX9_DISABLE_DISK_IO */
41172 /*
41173  * Return TRUE if we are dealing with the jx9:// stream.
41174  * FALSE otherwise.
41175  */
is_jx9_stream(const jx9_io_stream * pStream)41176 static int is_jx9_stream(const jx9_io_stream *pStream)
41177 {
41178 #ifndef JX9_DISABLE_DISK_IO
41179 	return pStream == &sjx9Stream;
41180 #else
41181 	SXUNUSED(pStream); /* cc warning */
41182 	return 0;
41183 #endif /* JX9_DISABLE_DISK_IO */
41184 }
41185 
41186 #endif /* JX9_DISABLE_BUILTIN_FUNC */
41187 /*
41188  * Export the IO routines defined above and the built-in IO streams
41189  * [i.e: file://, jx9://].
41190  * Note:
41191  *  If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
41192  *  defined then this function is a no-op.
41193  */
jx9RegisterIORoutine(jx9_vm * pVm)41194 JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
41195 {
41196 #ifndef JX9_DISABLE_BUILTIN_FUNC
41197 	      /* VFS functions */
41198 	static const jx9_builtin_func aVfsFunc[] = {
41199 		{"chdir",   jx9Vfs_chdir   },
41200 		{"chroot",  jx9Vfs_chroot  },
41201 		{"getcwd",  jx9Vfs_getcwd  },
41202 		{"rmdir",   jx9Vfs_rmdir   },
41203 		{"is_dir",  jx9Vfs_is_dir  },
41204 		{"mkdir",   jx9Vfs_mkdir   },
41205 		{"rename",  jx9Vfs_rename  },
41206 		{"realpath", jx9Vfs_realpath},
41207 		{"sleep",   jx9Vfs_sleep   },
41208 		{"usleep",  jx9Vfs_usleep  },
41209 		{"unlink",  jx9Vfs_unlink  },
41210 		{"delete",  jx9Vfs_unlink  },
41211 		{"chmod",   jx9Vfs_chmod   },
41212 		{"chown",   jx9Vfs_chown   },
41213 		{"chgrp",   jx9Vfs_chgrp   },
41214 		{"disk_free_space", jx9Vfs_disk_free_space  },
41215 		{"disk_total_space", jx9Vfs_disk_total_space},
41216 		{"file_exists", jx9Vfs_file_exists },
41217 		{"filesize",    jx9Vfs_file_size   },
41218 		{"fileatime",   jx9Vfs_file_atime  },
41219 		{"filemtime",   jx9Vfs_file_mtime  },
41220 		{"filectime",   jx9Vfs_file_ctime  },
41221 		{"is_file",     jx9Vfs_is_file  },
41222 		{"is_link",     jx9Vfs_is_link  },
41223 		{"is_readable", jx9Vfs_is_readable   },
41224 		{"is_writable", jx9Vfs_is_writable   },
41225 		{"is_executable", jx9Vfs_is_executable},
41226 		{"filetype",    jx9Vfs_filetype },
41227 		{"stat",        jx9Vfs_stat     },
41228 		{"lstat",       jx9Vfs_lstat    },
41229 		{"getenv",      jx9Vfs_getenv   },
41230 		{"setenv",      jx9Vfs_putenv   },
41231 		{"putenv",      jx9Vfs_putenv   },
41232 		{"touch",       jx9Vfs_touch    },
41233 		{"link",        jx9Vfs_link     },
41234 		{"symlink",     jx9Vfs_symlink  },
41235 		{"umask",       jx9Vfs_umask    },
41236 		{"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir },
41237 		{"get_current_user", jx9Vfs_get_current_user },
41238 		{"getpid",      jx9Vfs_getmypid },
41239 		{"getuid",      jx9Vfs_getmyuid },
41240 		{"getgid",      jx9Vfs_getmygid },
41241 		{"uname",       jx9Vfs_uname},
41242 		     /* Path processing */
41243 		{"dirname",     jx9Builtin_dirname  },
41244 		{"basename",    jx9Builtin_basename },
41245 		{"pathinfo",    jx9Builtin_pathinfo },
41246 		{"strglob",     jx9Builtin_strglob  },
41247 		{"fnmatch",     jx9Builtin_fnmatch  },
41248 		     /* ZIP processing */
41249 		{"zip_open",    jx9Builtin_zip_open },
41250 		{"zip_close",   jx9Builtin_zip_close},
41251 		{"zip_read",    jx9Builtin_zip_read },
41252 		{"zip_entry_open", jx9Builtin_zip_entry_open },
41253 		{"zip_entry_close", jx9Builtin_zip_entry_close},
41254 		{"zip_entry_name", jx9Builtin_zip_entry_name },
41255 		{"zip_entry_filesize",      jx9Builtin_zip_entry_filesize       },
41256 		{"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize },
41257 		{"zip_entry_read", jx9Builtin_zip_entry_read },
41258 		{"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor},
41259 		{"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
41260 	};
41261 	    /* IO stream functions */
41262 	static const jx9_builtin_func aIOFunc[] = {
41263 		{"ftruncate", jx9Builtin_ftruncate },
41264 		{"fseek",     jx9Builtin_fseek  },
41265 		{"ftell",     jx9Builtin_ftell  },
41266 		{"rewind",    jx9Builtin_rewind },
41267 		{"fflush",    jx9Builtin_fflush },
41268 		{"feof",      jx9Builtin_feof   },
41269 		{"fgetc",     jx9Builtin_fgetc  },
41270 		{"fgets",     jx9Builtin_fgets  },
41271 		{"fread",     jx9Builtin_fread  },
41272 		{"fgetcsv",   jx9Builtin_fgetcsv},
41273 		{"fgetss",    jx9Builtin_fgetss },
41274 		{"readdir",   jx9Builtin_readdir},
41275 		{"rewinddir", jx9Builtin_rewinddir },
41276 		{"closedir",  jx9Builtin_closedir},
41277 		{"opendir",   jx9Builtin_opendir },
41278 		{"readfile",  jx9Builtin_readfile},
41279 		{"file_get_contents", jx9Builtin_file_get_contents},
41280 		{"file_put_contents", jx9Builtin_file_put_contents},
41281 		{"file",      jx9Builtin_file   },
41282 		{"copy",      jx9Builtin_copy   },
41283 		{"fstat",     jx9Builtin_fstat  },
41284 		{"fwrite",    jx9Builtin_fwrite },
41285 		{"fputs",     jx9Builtin_fwrite },
41286 		{"flock",     jx9Builtin_flock  },
41287 		{"fclose",    jx9Builtin_fclose },
41288 		{"fopen",     jx9Builtin_fopen  },
41289 		{"fpassthru", jx9Builtin_fpassthru },
41290 		{"fputcsv",   jx9Builtin_fputcsv },
41291 		{"fprintf",   jx9Builtin_fprintf },
41292 #if !defined(JX9_DISABLE_HASH_FUNC)
41293 		{"md5_file",  jx9Builtin_md5_file},
41294 		{"sha1_file", jx9Builtin_sha1_file},
41295 #endif /* JX9_DISABLE_HASH_FUNC */
41296 		{"parse_ini_file", jx9Builtin_parse_ini_file},
41297 		{"vfprintf",  jx9Builtin_vfprintf}
41298 	};
41299 	const jx9_io_stream *pFileStream = 0;
41300 	sxu32 n = 0;
41301 	/* Register the functions defined above */
41302 	for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
41303 		jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
41304 	}
41305 	for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
41306 		jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
41307 	}
41308 #ifndef JX9_DISABLE_DISK_IO
41309 	/* Register the file stream if available */
41310 #ifdef __WINNT__
41311 	pFileStream = &sWinFileStream;
41312 #elif defined(__UNIXES__)
41313 	pFileStream = &sUnixFileStream;
41314 #endif
41315 	/* Install the jx9:// stream */
41316 	jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
41317 #endif /* JX9_DISABLE_DISK_IO */
41318 	if( pFileStream ){
41319 		/* Install the file:// stream */
41320 		jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
41321 	}
41322 #else
41323    SXUNUSED(pVm); /* cc warning */
41324 #endif /* JX9_DISABLE_BUILTIN_FUNC */
41325 	return SXRET_OK;
41326 }
41327 /*
41328  * Export the STDIN handle.
41329  */
jx9ExportStdin(jx9_vm * pVm)41330 JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
41331 {
41332 #ifndef JX9_DISABLE_BUILTIN_FUNC
41333 #ifndef JX9_DISABLE_DISK_IO
41334 	if( pVm->pStdin == 0  ){
41335 		io_private *pIn;
41336 		/* Allocate an IO private instance */
41337 		pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41338 		if( pIn == 0 ){
41339 			return 0;
41340 		}
41341 		InitIOPrivate(pVm, &sjx9Stream, pIn);
41342 		/* Initialize the handle */
41343 		pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
41344 		/* Install the STDIN stream */
41345 		pVm->pStdin = pIn;
41346 		return pIn;
41347 	}else{
41348 		/* NULL or STDIN */
41349 		return pVm->pStdin;
41350 	}
41351 #else
41352 	return 0;
41353 #endif
41354 #else
41355 	SXUNUSED(pVm); /* cc warning */
41356 	return 0;
41357 #endif
41358 }
41359 /*
41360  * Export the STDOUT handle.
41361  */
jx9ExportStdout(jx9_vm * pVm)41362 JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
41363 {
41364 #ifndef JX9_DISABLE_BUILTIN_FUNC
41365 #ifndef JX9_DISABLE_DISK_IO
41366 	if( pVm->pStdout == 0  ){
41367 		io_private *pOut;
41368 		/* Allocate an IO private instance */
41369 		pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41370 		if( pOut == 0 ){
41371 			return 0;
41372 		}
41373 		InitIOPrivate(pVm, &sjx9Stream, pOut);
41374 		/* Initialize the handle */
41375 		pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
41376 		/* Install the STDOUT stream */
41377 		pVm->pStdout = pOut;
41378 		return pOut;
41379 	}else{
41380 		/* NULL or STDOUT */
41381 		return pVm->pStdout;
41382 	}
41383 #else
41384 	return 0;
41385 #endif
41386 #else
41387 	SXUNUSED(pVm); /* cc warning */
41388 	return 0;
41389 #endif
41390 }
41391 /*
41392  * Export the STDERR handle.
41393  */
jx9ExportStderr(jx9_vm * pVm)41394 JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
41395 {
41396 #ifndef JX9_DISABLE_BUILTIN_FUNC
41397 #ifndef JX9_DISABLE_DISK_IO
41398 	if( pVm->pStderr == 0  ){
41399 		io_private *pErr;
41400 		/* Allocate an IO private instance */
41401 		pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
41402 		if( pErr == 0 ){
41403 			return 0;
41404 		}
41405 		InitIOPrivate(pVm, &sjx9Stream, pErr);
41406 		/* Initialize the handle */
41407 		pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
41408 		/* Install the STDERR stream */
41409 		pVm->pStderr = pErr;
41410 		return pErr;
41411 	}else{
41412 		/* NULL or STDERR */
41413 		return pVm->pStderr;
41414 	}
41415 #else
41416 	return 0;
41417 #endif
41418 #else
41419 	SXUNUSED(pVm); /* cc warning */
41420 	return 0;
41421 #endif
41422 }
41423 
41424 /* jx9_vm.c */
41425 /*
41426  * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
41427  * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
41428  * Version 1.7.2
41429  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
41430  * please contact Symisc Systems via:
41431  *       legal@symisc.net
41432  *       licensing@symisc.net
41433  *       contact@symisc.net
41434  * or visit:
41435  *      http://jx9.symisc.net/
41436  */
41437  /* $SymiscID: jx9_vm.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
41438 #ifndef JX9_AMALGAMATION
41439 #include "jx9Int.h"
41440 #endif
41441 /*
41442  * The code in this file implements execution method of the JX9 Virtual Machine.
41443  * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program
41444  * which is then executed by the virtual machine implemented here to do the work of the JX9
41445  * statements.
41446  * JX9 bytecode programs are similar in form to assembly language. The program consists
41447  * of a linear sequence of operations .Each operation has an opcode and 3 operands.
41448  * Operands P1 and P2 are integers where the first is signed while the second is unsigned.
41449  * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually
41450  * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions.
41451  * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands.
41452  * Computation results are stored on a stack. Each entry on the stack is of type jx9_value.
41453  * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
41454  * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
41455  * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
41456  * and so on.
41457  * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
41458  * Each jx9_value may cache multiple representations(string, integer etc.) of the same value.
41459  * An implicit conversion from one type to the other occurs as necessary.
41460  * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does
41461  * the work of interpreting a JX9 bytecode program. But other routines are also provided
41462  * to help in building up a program instruction by instruction.
41463  */
41464 /*
41465  * Each active virtual machine frame is represented by an instance
41466  * of the following structure.
41467  * VM Frame hold local variables and other stuff related to function call.
41468  */
41469 struct VmFrame
41470 {
41471 	VmFrame *pParent; /* Parent frame or NULL if global scope */
41472 	void *pUserData;  /* Upper layer private data associated with this frame */
41473 	SySet sLocal;     /* Local variables container (VmSlot instance) */
41474 	jx9_vm *pVm;      /* VM that own this frame */
41475 	SyHash hVar;      /* Variable hashtable for fast lookup */
41476 	SySet sArg;       /* Function arguments container */
41477 	sxi32 iFlags;     /* Frame configuration flags (See below)*/
41478 	sxu32 iExceptionJump; /* Exception jump destination */
41479 };
41480 /*
41481  * When a user defined variable is  garbage collected, memory object index
41482  * is stored in an instance of the following structure and put in the free object
41483  * table so that it can be reused again without allocating a new memory object.
41484  */
41485 typedef struct VmSlot VmSlot;
41486 struct VmSlot
41487 {
41488 	sxu32 nIdx;      /* Index in pVm->aMemObj[] */
41489 	void *pUserData; /* Upper-layer private data */
41490 };
41491 /*
41492  * Each parsed URI is recorded and stored in an instance of the following structure.
41493  * This structure and it's related routines are taken verbatim from the xHT project
41494  * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
41495  * the xHT project is developed internally by Symisc Systems.
41496  */
41497 typedef struct SyhttpUri SyhttpUri;
41498 struct SyhttpUri
41499 {
41500 	SyString sHost;     /* Hostname or IP address */
41501 	SyString sPort;     /* Port number */
41502 	SyString sPath;     /* Mandatory resource path passed verbatim (Not decoded) */
41503 	SyString sQuery;    /* Query part */
41504 	SyString sFragment; /* Fragment part */
41505 	SyString sScheme;   /* Scheme */
41506 	SyString sUser;     /* Username */
41507 	SyString sPass;     /* Password */
41508 	SyString sRaw;      /* Raw URI */
41509 };
41510 /*
41511  * An instance of the following structure is used to record all MIME headers seen
41512  * during a HTTP interaction.
41513  * This structure and it's related routines are taken verbatim from the xHT project
41514  * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
41515  * the xHT project is developed internally by Symisc Systems.
41516  */
41517 typedef struct SyhttpHeader SyhttpHeader;
41518 struct SyhttpHeader
41519 {
41520 	SyString sName;    /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */
41521 	SyString sValue;   /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */
41522 };
41523 /*
41524  * Supported HTTP methods.
41525  */
41526 #define HTTP_METHOD_GET  1 /* GET */
41527 #define HTTP_METHOD_HEAD 2 /* HEAD */
41528 #define HTTP_METHOD_POST 3 /* POST */
41529 #define HTTP_METHOD_PUT  4 /* PUT */
41530 #define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/
41531 /*
41532  * Supported HTTP protocol version.
41533  */
41534 #define HTTP_PROTO_10 1 /* HTTP/1.0 */
41535 #define HTTP_PROTO_11 2 /* HTTP/1.1 */
41536 /*
41537  * Register a constant and it's associated expansion callback so that
41538  * it can be expanded from the target JX9 program.
41539  * The constant expansion mechanism under JX9 is extremely powerful yet
41540  * simple and work as follows:
41541  * Each registered constant have a C procedure associated with it.
41542  * This procedure known as the constant expansion callback is responsible
41543  * of expanding the invoked constant to the desired value, for example:
41544  * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
41545  * The "__OS__" constant procedure expands to the name of the host Operating Systems
41546  * (Windows, Linux, ...) and so on.
41547  * Please refer to the official documentation for additional information.
41548  */
jx9VmRegisterConstant(jx9_vm * pVm,const SyString * pName,ProcConstant xExpand,void * pUserData)41549 JX9_PRIVATE sxi32 jx9VmRegisterConstant(
41550 	jx9_vm *pVm,            /* Target VM */
41551 	const SyString *pName,  /* Constant name */
41552 	ProcConstant xExpand,   /* Constant expansion callback */
41553 	void *pUserData         /* Last argument to xExpand() */
41554 	)
41555 {
41556 	jx9_constant *pCons;
41557 	SyHashEntry *pEntry;
41558 	char *zDupName;
41559 	sxi32 rc;
41560 	pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte);
41561 	if( pEntry ){
41562 		/* Overwrite the old definition and return immediately */
41563 		pCons = (jx9_constant *)pEntry->pUserData;
41564 		pCons->xExpand = xExpand;
41565 		pCons->pUserData = pUserData;
41566 		return SXRET_OK;
41567 	}
41568 	/* Allocate a new constant instance */
41569 	pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant));
41570 	if( pCons == 0 ){
41571 		return 0;
41572 	}
41573 	/* Duplicate constant name */
41574 	zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
41575 	if( zDupName == 0 ){
41576 		SyMemBackendPoolFree(&pVm->sAllocator, pCons);
41577 		return 0;
41578 	}
41579 	/* Install the constant */
41580 	SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
41581 	pCons->xExpand = xExpand;
41582 	pCons->pUserData = pUserData;
41583 	rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
41584 	if( rc != SXRET_OK ){
41585 		SyMemBackendFree(&pVm->sAllocator, zDupName);
41586 		SyMemBackendPoolFree(&pVm->sAllocator, pCons);
41587 		return rc;
41588 	}
41589 	/* All done, constant can be invoked from JX9 code */
41590 	return SXRET_OK;
41591 }
41592 /*
41593  * Allocate a new foreign function instance.
41594  * This function return SXRET_OK on success. Any other
41595  * return value indicates failure.
41596  * Please refer to the official documentation for an introduction to
41597  * the foreign function mechanism.
41598  */
jx9NewForeignFunction(jx9_vm * pVm,const SyString * pName,ProcHostFunction xFunc,void * pUserData,jx9_user_func ** ppOut)41599 static sxi32 jx9NewForeignFunction(
41600 	jx9_vm *pVm,              /* Target VM */
41601 	const SyString *pName,    /* Foreign function name */
41602 	ProcHostFunction xFunc,  /* Foreign function implementation */
41603 	void *pUserData,          /* Foreign function private data */
41604 	jx9_user_func **ppOut     /* OUT: VM image of the foreign function */
41605 	)
41606 {
41607 	jx9_user_func *pFunc;
41608 	char *zDup;
41609 	/* Allocate a new user function */
41610 	pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func));
41611 	if( pFunc == 0 ){
41612 		return SXERR_MEM;
41613 	}
41614 	/* Duplicate function name */
41615 	zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
41616 	if( zDup == 0 ){
41617 		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
41618 		return SXERR_MEM;
41619 	}
41620 	/* Zero the structure */
41621 	SyZero(pFunc, sizeof(jx9_user_func));
41622 	/* Initialize structure fields */
41623 	SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
41624 	pFunc->pVm   = pVm;
41625 	pFunc->xFunc = xFunc;
41626 	pFunc->pUserData = pUserData;
41627 	SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data));
41628 	/* Write a pointer to the new function */
41629 	*ppOut = pFunc;
41630 	return SXRET_OK;
41631 }
41632 /*
41633  * Install a foreign function and it's associated callback so that
41634  * it can be invoked from the target JX9 code.
41635  * This function return SXRET_OK on successful registration. Any other
41636  * return value indicates failure.
41637  * Please refer to the official documentation for an introduction to
41638  * the foreign function mechanism.
41639  */
jx9VmInstallForeignFunction(jx9_vm * pVm,const SyString * pName,ProcHostFunction xFunc,void * pUserData)41640 JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
41641 	jx9_vm *pVm,              /* Target VM */
41642 	const SyString *pName,    /* Foreign function name */
41643 	ProcHostFunction xFunc,  /* Foreign function implementation */
41644 	void *pUserData           /* Foreign function private data */
41645 	)
41646 {
41647 	jx9_user_func *pFunc;
41648 	SyHashEntry *pEntry;
41649 	sxi32 rc;
41650 	/* Overwrite any previously registered function with the same name */
41651 	pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
41652 	if( pEntry ){
41653 		pFunc = (jx9_user_func *)pEntry->pUserData;
41654 		pFunc->pUserData = pUserData;
41655 		pFunc->xFunc = xFunc;
41656 		SySetReset(&pFunc->aAux);
41657 		return SXRET_OK;
41658 	}
41659 	/* Create a new user function */
41660 	rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
41661 	if( rc != SXRET_OK ){
41662 		return rc;
41663 	}
41664 	/* Install the function in the corresponding hashtable */
41665 	rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
41666 	if( rc != SXRET_OK ){
41667 		SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
41668 		SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
41669 		return rc;
41670 	}
41671 	/* User function successfully installed */
41672 	return SXRET_OK;
41673 }
41674 /*
41675  * Initialize a VM function.
41676  */
jx9VmInitFuncState(jx9_vm * pVm,jx9_vm_func * pFunc,const char * zName,sxu32 nByte,sxi32 iFlags,void * pUserData)41677 JX9_PRIVATE sxi32 jx9VmInitFuncState(
41678 	jx9_vm *pVm,        /* Target VM */
41679 	jx9_vm_func *pFunc, /* Target Fucntion */
41680 	const char *zName,  /* Function name */
41681 	sxu32 nByte,        /* zName length */
41682 	sxi32 iFlags,       /* Configuration flags */
41683 	void *pUserData     /* Function private data */
41684 	)
41685 {
41686 	/* Zero the structure */
41687 	SyZero(pFunc, sizeof(jx9_vm_func));
41688 	/* Initialize structure fields */
41689 	/* Arguments container */
41690 	SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
41691 	/* Static variable container */
41692 	SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
41693 	/* Bytecode container */
41694 	SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
41695     /* Preallocate some instruction slots */
41696 	SySetAlloc(&pFunc->aByteCode, 0x10);
41697 	pFunc->iFlags = iFlags;
41698 	pFunc->pUserData = pUserData;
41699 	SyStringInitFromBuf(&pFunc->sName, zName, nByte);
41700 	return SXRET_OK;
41701 }
41702 /*
41703  * Install a user defined function in the corresponding VM container.
41704  */
jx9VmInstallUserFunction(jx9_vm * pVm,jx9_vm_func * pFunc,SyString * pName)41705 JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
41706 	jx9_vm *pVm,        /* Target VM */
41707 	jx9_vm_func *pFunc, /* Target function */
41708 	SyString *pName     /* Function name */
41709 	)
41710 {
41711 	SyHashEntry *pEntry;
41712 	sxi32 rc;
41713 	if( pName == 0 ){
41714 		/* Use the built-in name */
41715 		pName = &pFunc->sName;
41716 	}
41717 	/* Check for duplicates (functions with the same name) first */
41718 	pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
41719 	if( pEntry ){
41720 		jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData;
41721 		if( pLink != pFunc ){
41722 			/* Link */
41723 			pFunc->pNextName = pLink;
41724 			pEntry->pUserData = pFunc;
41725 		}
41726 		return SXRET_OK;
41727 	}
41728 	/* First time seen */
41729 	pFunc->pNextName = 0;
41730 	rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
41731 	return rc;
41732 }
41733 /*
41734  * Instruction builder interface.
41735  */
jx9VmEmitInstr(jx9_vm * pVm,sxi32 iOp,sxi32 iP1,sxu32 iP2,void * p3,sxu32 * pIndex)41736 JX9_PRIVATE sxi32 jx9VmEmitInstr(
41737 	jx9_vm *pVm,  /* Target VM */
41738 	sxi32 iOp,    /* Operation to perform */
41739 	sxi32 iP1,    /* First operand */
41740 	sxu32 iP2,    /* Second operand */
41741 	void *p3,     /* Third operand */
41742 	sxu32 *pIndex /* Instruction index. NULL otherwise */
41743 	)
41744 {
41745 	VmInstr sInstr;
41746 	sxi32 rc;
41747 	/* Fill the VM instruction */
41748 	sInstr.iOp = (sxu8)iOp;
41749 	sInstr.iP1 = iP1;
41750 	sInstr.iP2 = iP2;
41751 	sInstr.p3  = p3;
41752 	if( pIndex ){
41753 		/* Instruction index in the bytecode array */
41754 		*pIndex = SySetUsed(pVm->pByteContainer);
41755 	}
41756 	/* Finally, record the instruction */
41757 	rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
41758 	if( rc != SXRET_OK ){
41759 		jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure");
41760 		/* Fall throw */
41761 	}
41762 	return rc;
41763 }
41764 /*
41765  * Swap the current bytecode container with the given one.
41766  */
jx9VmSetByteCodeContainer(jx9_vm * pVm,SySet * pContainer)41767 JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer)
41768 {
41769 	if( pContainer == 0 ){
41770 		/* Point to the default container */
41771 		pVm->pByteContainer = &pVm->aByteCode;
41772 	}else{
41773 		/* Change container */
41774 		pVm->pByteContainer = &(*pContainer);
41775 	}
41776 	return SXRET_OK;
41777 }
41778 /*
41779  * Return the current bytecode container.
41780  */
jx9VmGetByteCodeContainer(jx9_vm * pVm)41781 JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
41782 {
41783 	return pVm->pByteContainer;
41784 }
41785 /*
41786  * Extract the VM instruction rooted at nIndex.
41787  */
jx9VmGetInstr(jx9_vm * pVm,sxu32 nIndex)41788 JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
41789 {
41790 	VmInstr *pInstr;
41791 	pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
41792 	return pInstr;
41793 }
41794 /*
41795  * Return the total number of VM instructions recorded so far.
41796  */
jx9VmInstrLength(jx9_vm * pVm)41797 JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
41798 {
41799 	return SySetUsed(pVm->pByteContainer);
41800 }
41801 /*
41802  * Pop the last VM instruction.
41803  */
jx9VmPopInstr(jx9_vm * pVm)41804 JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
41805 {
41806 	return (VmInstr *)SySetPop(pVm->pByteContainer);
41807 }
41808 /*
41809  * Peek the last VM instruction.
41810  */
jx9VmPeekInstr(jx9_vm * pVm)41811 JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
41812 {
41813 	return (VmInstr *)SySetPeek(pVm->pByteContainer);
41814 }
41815 /*
41816  * Allocate a new virtual machine frame.
41817  */
VmNewFrame(jx9_vm * pVm,void * pUserData)41818 static VmFrame * VmNewFrame(
41819 	jx9_vm *pVm,              /* Target VM */
41820 	void *pUserData          /* Upper-layer private data */
41821 	)
41822 {
41823 	VmFrame *pFrame;
41824 	/* Allocate a new vm frame */
41825 	pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
41826 	if( pFrame == 0 ){
41827 		return 0;
41828 	}
41829 	/* Zero the structure */
41830 	SyZero(pFrame, sizeof(VmFrame));
41831 	/* Initialize frame fields */
41832 	pFrame->pUserData = pUserData;
41833 	pFrame->pVm = pVm;
41834 	SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
41835 	SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
41836 	SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
41837 	return pFrame;
41838 }
41839 /*
41840  * Enter a VM frame.
41841  */
VmEnterFrame(jx9_vm * pVm,void * pUserData,VmFrame ** ppFrame)41842 static sxi32 VmEnterFrame(
41843 	jx9_vm *pVm,               /* Target VM */
41844 	void *pUserData,           /* Upper-layer private data */
41845 	VmFrame **ppFrame          /* OUT: Top most active frame */
41846 	)
41847 {
41848 	VmFrame *pFrame;
41849 	/* Allocate a new frame */
41850 	pFrame = VmNewFrame(&(*pVm), pUserData);
41851 	if( pFrame == 0 ){
41852 		return SXERR_MEM;
41853 	}
41854 	/* Link to the list of active VM frame */
41855 	pFrame->pParent = pVm->pFrame;
41856 	pVm->pFrame = pFrame;
41857 	if( ppFrame ){
41858 		/* Write a pointer to the new VM frame */
41859 		*ppFrame = pFrame;
41860 	}
41861 	return SXRET_OK;
41862 }
41863 /*
41864  * Link a foreign variable with the TOP most active frame.
41865  * Refer to the JX9_OP_UPLINK instruction implementation for more
41866  * information.
41867  */
VmFrameLink(jx9_vm * pVm,SyString * pName)41868 static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
41869 {
41870 	VmFrame *pTarget, *pFrame;
41871 	SyHashEntry *pEntry = 0;
41872 	sxi32 rc;
41873 	/* Point to the upper frame */
41874 	pFrame = pVm->pFrame;
41875 	pTarget = pFrame;
41876 	pFrame = pTarget->pParent;
41877 	while( pFrame ){
41878 		/* Query the current frame */
41879 		pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
41880 		if( pEntry ){
41881 			/* Variable found */
41882 			break;
41883 		}
41884 		/* Point to the upper frame */
41885 		pFrame = pFrame->pParent;
41886 	}
41887 	if( pEntry == 0 ){
41888 		/* Inexistant variable */
41889 		return SXERR_NOTFOUND;
41890 	}
41891 	/* Link to the current frame */
41892 	rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData);
41893 	return rc;
41894 }
41895 /*
41896  * Leave the top-most active frame.
41897  */
VmLeaveFrame(jx9_vm * pVm)41898 static void VmLeaveFrame(jx9_vm *pVm)
41899 {
41900 	VmFrame *pFrame = pVm->pFrame;
41901 	if( pFrame ){
41902 		/* Unlink from the list of active VM frame */
41903 		pVm->pFrame = pFrame->pParent;
41904 		if( pFrame->pParent  ){
41905 			VmSlot  *aSlot;
41906 			sxu32 n;
41907 			/* Restore local variable to the free pool so that they can be reused again */
41908 			aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
41909 			for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){
41910 				/* Unset the local variable */
41911 				jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
41912 			}
41913 		}
41914 		/* Release internal containers */
41915 		SyHashRelease(&pFrame->hVar);
41916 		SySetRelease(&pFrame->sArg);
41917 		SySetRelease(&pFrame->sLocal);
41918 		/* Release the whole structure */
41919 		SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
41920 	}
41921 }
41922 /*
41923  * Compare two functions signature and return the comparison result.
41924  */
VmOverloadCompare(SyString * pFirst,SyString * pSecond)41925 static int VmOverloadCompare(SyString *pFirst, SyString *pSecond)
41926 {
41927 	const char *zSend = &pSecond->zString[pSecond->nByte];
41928 	const char *zFend = &pFirst->zString[pFirst->nByte];
41929 	const char *zSin = pSecond->zString;
41930 	const char *zFin = pFirst->zString;
41931 	const char *zPtr = zFin;
41932 	for(;;){
41933 		if( zFin >= zFend || zSin >= zSend ){
41934 			break;
41935 		}
41936 		if( zFin[0] != zSin[0] ){
41937 			/* mismatch */
41938 			break;
41939 		}
41940 		zFin++;
41941 		zSin++;
41942 	}
41943 	return (int)(zFin-zPtr);
41944 }
41945 /*
41946  * Select the appropriate VM function for the current call context.
41947  * This is the implementation of the powerful 'function overloading' feature
41948  * introduced by the version 2 of the JX9 engine.
41949  * Refer to the official documentation for more information.
41950  */
VmOverload(jx9_vm * pVm,jx9_vm_func * pList,jx9_value * aArg,int nArg)41951 static jx9_vm_func * VmOverload(
41952 	jx9_vm *pVm,         /* Target VM */
41953 	jx9_vm_func *pList,  /* Linked list of candidates for overloading */
41954 	jx9_value *aArg,     /* Array of passed arguments */
41955 	int nArg             /* Total number of passed arguments  */
41956 	)
41957 {
41958 	int iTarget, i, j, iCur, iMax;
41959 	jx9_vm_func *apSet[10];   /* Maximum number of candidates */
41960 	jx9_vm_func *pLink;
41961 	SyString sArgSig;
41962 	SyBlob sSig;
41963 
41964 	pLink = pList;
41965 	i = 0;
41966 	/* Put functions expecting the same number of passed arguments */
41967 	while( i < (int)SX_ARRAYSIZE(apSet) ){
41968 		if( pLink == 0 ){
41969 			break;
41970 		}
41971 		if( (int)SySetUsed(&pLink->aArgs) == nArg ){
41972 			/* Candidate for overloading */
41973 			apSet[i++] = pLink;
41974 		}
41975 		/* Point to the next entry */
41976 		pLink = pLink->pNextName;
41977 	}
41978 	if( i < 1 ){
41979 		/* No candidates, return the head of the list */
41980 		return pList;
41981 	}
41982 	if( nArg < 1 || i < 2 ){
41983 		/* Return the only candidate */
41984 		return apSet[0];
41985 	}
41986 	/* Calculate function signature */
41987 	SyBlobInit(&sSig, &pVm->sAllocator);
41988 	for( j = 0 ; j < nArg ; j++ ){
41989 		int c = 'n'; /* null */
41990 		if( aArg[j].iFlags & MEMOBJ_HASHMAP ){
41991 			/* Hashmap */
41992 			c = 'h';
41993 		}else if( aArg[j].iFlags & MEMOBJ_BOOL ){
41994 			/* bool */
41995 			c = 'b';
41996 		}else if( aArg[j].iFlags & MEMOBJ_INT ){
41997 			/* int */
41998 			c = 'i';
41999 		}else if( aArg[j].iFlags & MEMOBJ_STRING ){
42000 			/* String */
42001 			c = 's';
42002 		}else if( aArg[j].iFlags & MEMOBJ_REAL ){
42003 			/* Float */
42004 			c = 'f';
42005 		}
42006 		if( c > 0 ){
42007 			SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
42008 		}
42009 	}
42010 	SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
42011 	iTarget = 0;
42012 	iMax = -1;
42013 	/* Select the appropriate function */
42014 	for( j = 0 ; j < i ; j++ ){
42015 		/* Compare the two signatures */
42016 		iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
42017 		if( iCur > iMax ){
42018 			iMax = iCur;
42019 			iTarget = j;
42020 		}
42021 	}
42022 	SyBlobRelease(&sSig);
42023 	/* Appropriate function for the current call context */
42024 	return apSet[iTarget];
42025 }
42026 /*
42027  * Dummy read-only buffer used for slot reservation.
42028  */
42029 static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */
42030 /*
42031  * Reserve a constant memory object.
42032  * Return a pointer to the raw jx9_value on success. NULL on failure.
42033  */
jx9VmReserveConstObj(jx9_vm * pVm,sxu32 * pIndex)42034 JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
42035 {
42036 	jx9_value *pObj;
42037 	sxi32 rc;
42038 	if( pIndex ){
42039 		/* Object index in the object table */
42040 		*pIndex = SySetUsed(&pVm->aLitObj);
42041 	}
42042 	/* Reserve a slot for the new object */
42043 	rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
42044 	if( rc != SXRET_OK ){
42045 		/* If the supplied memory subsystem is so sick that we are unable to allocate
42046 		 * a tiny chunk of memory, there is no much we can do here.
42047 		 */
42048 		return 0;
42049 	}
42050 	pObj = (jx9_value *)SySetPeek(&pVm->aLitObj);
42051 	return pObj;
42052 }
42053 /*
42054  * Reserve a memory object.
42055  * Return a pointer to the raw jx9_value on success. NULL on failure.
42056  */
VmReserveMemObj(jx9_vm * pVm,sxu32 * pIndex)42057 static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
42058 {
42059 	jx9_value *pObj;
42060 	sxi32 rc;
42061 	if( pIndex ){
42062 		/* Object index in the object table */
42063 		*pIndex = SySetUsed(&pVm->aMemObj);
42064 	}
42065 	/* Reserve a slot for the new object */
42066 	rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
42067 	if( rc != SXRET_OK ){
42068 		/* If the supplied memory subsystem is so sick that we are unable to allocate
42069 		 * a tiny chunk of memory, there is no much we can do here.
42070 		 */
42071 		return 0;
42072 	}
42073 	pObj = (jx9_value *)SySetPeek(&pVm->aMemObj);
42074 	return pObj;
42075 }
42076 /* Forward declaration */
42077 static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
42078 /*
42079  * Built-in functions that cannot be implemented directly as foreign functions.
42080  */
42081 #define JX9_BUILTIN_LIB \
42082 	"function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
42083     "{"\
42084 	"  if( func_num_args() < 1 ){ return FALSE; }"\
42085 	"  $aDir = [];"\
42086 	"  $pHandle = opendir($directory);"\
42087 	"  if( $pHandle == FALSE ){ return FALSE; }"\
42088 	"  while(FALSE !== ($pEntry = readdir($pHandle)) ){"\
42089 	"      $aDir[] = $pEntry;"\
42090 	"   }"\
42091 	"  closedir($pHandle);"\
42092 	"  if( $sort_order == SCANDIR_SORT_DESCENDING ){"\
42093 	"      rsort($aDir);"\
42094 	"  }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\
42095 	"      sort($aDir);"\
42096 	"  }"\
42097 	"  return $aDir;"\
42098 	"}"\
42099 	"function glob(string $pattern, int $iFlags = 0){"\
42100 	"/* Open the target directory */"\
42101 	"$zDir = dirname($pattern);"\
42102 	"if(!is_string($zDir) ){ $zDir = './'; }"\
42103 	"$pHandle = opendir($zDir);"\
42104 	"if( $pHandle == FALSE ){"\
42105 	"   /* IO error while opening the current directory, return FALSE */"\
42106 	"	return FALSE;"\
42107 	"}"\
42108 	"$pattern = basename($pattern);"\
42109 	"$pArray = []; /* Empty array */"\
42110 	"/* Loop throw available entries */"\
42111 	"while( FALSE !== ($pEntry = readdir($pHandle)) ){"\
42112 	" /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\
42113 	"	$rc = strglob($pattern, $pEntry);"\
42114 	"	if( $rc ){"\
42115 	"	   if( is_dir($pEntry) ){"\
42116 	"	      if( $iFlags & GLOB_MARK ){"\
42117 	"		     /* Adds a slash to each directory returned */"\
42118 	"			 $pEntry .= DIRECTORY_SEPARATOR;"\
42119 	"		  }"\
42120 	"	   }else if( $iFlags & GLOB_ONLYDIR ){"\
42121 	"	     /* Not a directory, ignore */"\
42122 	"		 continue;"\
42123 	"	   }"\
42124 	"	   /* Add the entry */"\
42125 	"	   $pArray[] = $pEntry;"\
42126 	"	}"\
42127 	" }"\
42128 	"/* Close the handle */"\
42129 	"closedir($pHandle);"\
42130 	"if( ($iFlags & GLOB_NOSORT) == 0 ){"\
42131 	"  /* Sort the array */"\
42132 	"  sort($pArray);"\
42133 	"}"\
42134 	"if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\
42135 	"  /* Return the search pattern if no files matching were found */"\
42136 	"  $pArray[] = $pattern;"\
42137 	"}"\
42138 	"/* Return the created array */"\
42139 	"return $pArray;"\
42140    "}"\
42141    "/* Creates a temporary file */"\
42142    "function tmpfile(){"\
42143    "  /* Extract the temp directory */"\
42144    "  $zTempDir = sys_get_temp_dir();"\
42145    "  if( strlen($zTempDir) < 1 ){"\
42146    "    /* Use the current dir */"\
42147    "    $zTempDir = '.';"\
42148    "  }"\
42149    "  /* Create the file */"\
42150    "  $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\
42151    "  return $pHandle;"\
42152    "}"\
42153    "/* Creates a temporary filename */"\
42154    "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
42155    "{"\
42156    "   return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
42157    "}"\
42158 	"function max(){"\
42159     "  $pArgs = func_get_args();"\
42160     " if( sizeof($pArgs) < 1 ){"\
42161 	"  return null;"\
42162     " }"\
42163     " if( sizeof($pArgs) < 2 ){"\
42164     " $pArg = $pArgs[0];"\
42165 	" if( !is_array($pArg) ){"\
42166 	"   return $pArg; "\
42167 	" }"\
42168 	" if( sizeof($pArg) < 1 ){"\
42169 	"   return null;"\
42170 	" }"\
42171 	" $pArg = array_copy($pArgs[0]);"\
42172 	" reset($pArg);"\
42173 	" $max = current($pArg);"\
42174 	" while( FALSE !== ($val = next($pArg)) ){"\
42175 	"   if( $val > $max ){"\
42176 	"     $max = $val;"\
42177     " }"\
42178 	" }"\
42179 	" return $max;"\
42180     " }"\
42181     " $max = $pArgs[0];"\
42182     " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
42183     " $val = $pArgs[$i];"\
42184 	"if( $val > $max ){"\
42185 	" $max = $val;"\
42186 	"}"\
42187     " }"\
42188 	" return $max;"\
42189     "}"\
42190 	"function min(){"\
42191     "  $pArgs = func_get_args();"\
42192     " if( sizeof($pArgs) < 1 ){"\
42193 	"  return null;"\
42194     " }"\
42195     " if( sizeof($pArgs) < 2 ){"\
42196     " $pArg = $pArgs[0];"\
42197 	" if( !is_array($pArg) ){"\
42198 	"   return $pArg; "\
42199 	" }"\
42200 	" if( sizeof($pArg) < 1 ){"\
42201 	"   return null;"\
42202 	" }"\
42203 	" $pArg = array_copy($pArgs[0]);"\
42204 	" reset($pArg);"\
42205 	" $min = current($pArg);"\
42206 	" while( FALSE !== ($val = next($pArg)) ){"\
42207 	"   if( $val < $min ){"\
42208 	"     $min = $val;"\
42209     " }"\
42210 	" }"\
42211 	" return $min;"\
42212     " }"\
42213     " $min = $pArgs[0];"\
42214     " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
42215     " $val = $pArgs[$i];"\
42216 	"if( $val < $min ){"\
42217 	" $min = $val;"\
42218 	" }"\
42219     " }"\
42220 	" return $min;"\
42221 	"}"
42222 /*
42223  * Initialize a freshly allocated JX9 Virtual Machine so that we can
42224  * start compiling the target JX9 program.
42225  */
jx9VmInit(jx9_vm * pVm,jx9 * pEngine)42226 JX9_PRIVATE sxi32 jx9VmInit(
42227 	 jx9_vm *pVm, /* Initialize this */
42228 	 jx9 *pEngine /* Master engine */
42229 	 )
42230 {
42231 	SyString sBuiltin;
42232 	jx9_value *pObj;
42233 	sxi32 rc;
42234 	/* Zero the structure */
42235 	SyZero(pVm, sizeof(jx9_vm));
42236 	/* Initialize VM fields */
42237 	pVm->pEngine = &(*pEngine);
42238 	SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator);
42239 	/* Instructions containers */
42240 	SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
42241 	SySetAlloc(&pVm->aByteCode, 0xFF);
42242 	pVm->pByteContainer = &pVm->aByteCode;
42243 	/* Object containers */
42244 	SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value));
42245 	SySetAlloc(&pVm->aMemObj, 0xFF);
42246 	/* Virtual machine internal containers */
42247 	SyBlobInit(&pVm->sConsumer, &pVm->sAllocator);
42248 	SyBlobInit(&pVm->sWorker, &pVm->sAllocator);
42249 	SyBlobInit(&pVm->sArgv, &pVm->sAllocator);
42250 	SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value));
42251 	SySetAlloc(&pVm->aLitObj, 0xFF);
42252 	SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
42253 	SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
42254 	SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
42255 	SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
42256 	SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
42257 	/* Configuration containers */
42258 	SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
42259 	SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
42260 	SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
42261 	SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
42262 	/* Error callbacks containers */
42263 	jx9MemObjInit(&(*pVm), &pVm->sAssertCallback);
42264 	/* Set a default recursion limit */
42265 #if defined(__WINNT__) || defined(__UNIXES__)
42266 	pVm->nMaxDepth = 32;
42267 #else
42268 	pVm->nMaxDepth = 16;
42269 #endif
42270 	/* Default assertion flags */
42271 	pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
42272 	/* PRNG context */
42273 	SyRandomnessInit(&pVm->sPrng, 0, 0);
42274 	/* Install the null constant */
42275 	pObj = jx9VmReserveConstObj(&(*pVm), 0);
42276 	if( pObj == 0 ){
42277 		rc = SXERR_MEM;
42278 		goto Err;
42279 	}
42280 	jx9MemObjInit(pVm, pObj);
42281 	/* Install the boolean TRUE constant */
42282 	pObj = jx9VmReserveConstObj(&(*pVm), 0);
42283 	if( pObj == 0 ){
42284 		rc = SXERR_MEM;
42285 		goto Err;
42286 	}
42287 	jx9MemObjInitFromBool(pVm, pObj, 1);
42288 	/* Install the boolean FALSE constant */
42289 	pObj = jx9VmReserveConstObj(&(*pVm), 0);
42290 	if( pObj == 0 ){
42291 		rc = SXERR_MEM;
42292 		goto Err;
42293 	}
42294 	jx9MemObjInitFromBool(pVm, pObj, 0);
42295 	/* Create the global frame */
42296 	rc = VmEnterFrame(&(*pVm), 0, 0);
42297 	if( rc != SXRET_OK ){
42298 		goto Err;
42299 	}
42300 	/* Initialize the code generator */
42301 	rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
42302 	if( rc != SXRET_OK ){
42303 		goto Err;
42304 	}
42305 	/* VM correctly initialized, set the magic number */
42306 	pVm->nMagic = JX9_VM_INIT;
42307 	SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
42308 	/* Compile the built-in library */
42309 	VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
42310 	/* Reset the code generator */
42311 	jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData);
42312 	return SXRET_OK;
42313 Err:
42314 	SyMemBackendRelease(&pVm->sAllocator);
42315 	return rc;
42316 }
42317 /*
42318  * Default VM output consumer callback.That is, all VM output is redirected to this
42319  * routine which store the output in an internal blob.
42320  * The output can be extracted later after program execution [jx9_vm_exec()] via
42321  * the [jx9_vm_config()] interface with a configuration verb set to
42322  * jx9VM_CONFIG_EXTRACT_OUTPUT.
42323  * Refer to the official docurmentation for additional information.
42324  * Note that for performance reason it's preferable to install a VM output
42325  * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
42326  * to finish executing and extracting the output.
42327  */
jx9VmBlobConsumer(const void * pOut,unsigned int nLen,void * pUserData)42328 JX9_PRIVATE sxi32 jx9VmBlobConsumer(
42329 	const void *pOut,   /* VM Generated output*/
42330 	unsigned int nLen,  /* Generated output length */
42331 	void *pUserData     /* User private data */
42332 	)
42333 {
42334 	 sxi32 rc;
42335 	 /* Store the output in an internal BLOB */
42336 	 rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
42337 	 return rc;
42338 }
42339 #define VM_STACK_GUARD 16
42340 /*
42341  * Allocate a new operand stack so that we can start executing
42342  * our compiled JX9 program.
42343  * Return a pointer to the operand stack (array of jx9_values)
42344  * on success. NULL (Fatal error) on failure.
42345  */
VmNewOperandStack(jx9_vm * pVm,sxu32 nInstr)42346 static jx9_value * VmNewOperandStack(
42347 	jx9_vm *pVm, /* Target VM */
42348 	sxu32 nInstr /* Total numer of generated bytecode instructions */
42349 	)
42350 {
42351 	jx9_value *pStack;
42352   /* No instruction ever pushes more than a single element onto the
42353   ** stack and the stack never grows on successive executions of the
42354   ** same loop. So the total number of instructions is an upper bound
42355   ** on the maximum stack depth required.
42356   **
42357   ** Allocation all the stack space we will ever need.
42358   */
42359 	nInstr += VM_STACK_GUARD;
42360 	pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
42361 	if( pStack == 0 ){
42362 		return 0;
42363 	}
42364 	/* Initialize the operand stack */
42365 	while( nInstr > 0 ){
42366 		jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
42367 		--nInstr;
42368 	}
42369 	/* Ready for bytecode execution */
42370 	return pStack;
42371 }
42372 /* Forward declaration */
42373 static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
42374 /*
42375  * Prepare the Virtual Machine for bytecode execution.
42376  * This routine gets called by the JX9 engine after
42377  * successful compilation of the target JX9 program.
42378  */
jx9VmMakeReady(jx9_vm * pVm)42379 JX9_PRIVATE sxi32 jx9VmMakeReady(
42380 	jx9_vm *pVm /* Target VM */
42381 	)
42382 {
42383 	sxi32 rc;
42384 	if( pVm->nMagic != JX9_VM_INIT ){
42385 		/* Initialize your VM first */
42386 		return SXERR_CORRUPT;
42387 	}
42388 	/* Mark the VM ready for bytecode execution */
42389 	pVm->nMagic = JX9_VM_RUN;
42390 	/* Release the code generator now we have compiled our program */
42391 	jx9ResetCodeGenerator(pVm, 0, 0);
42392 	/* Emit the DONE instruction */
42393 	rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
42394 	if( rc != SXRET_OK ){
42395 		return SXERR_MEM;
42396 	}
42397 	/* Script return value */
42398 	jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */
42399 	/* Allocate a new operand stack */
42400 	pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer));
42401 	if( pVm->aOps == 0 ){
42402 		return SXERR_MEM;
42403 	}
42404 	/* Set the default VM output consumer callback and it's
42405 	 * private data. */
42406 	pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer;
42407 	pVm->sVmConsumer.pUserData = &pVm->sConsumer;
42408 	/* Register special functions first [i.e: print, func_get_args(), die, etc.] */
42409 	rc = VmRegisterSpecialFunction(&(*pVm));
42410 	if( rc != SXRET_OK ){
42411 		/* Don't worry about freeing memory, everything will be released shortly */
42412 		return rc;
42413 	}
42414 	/* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */
42415 	rc = jx9HashmapLoadBuiltin(&(*pVm));
42416 	if( rc != SXRET_OK ){
42417 		/* Don't worry about freeing memory, everything will be released shortly */
42418 		return rc;
42419 	}
42420 	/* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
42421 	jx9RegisterBuiltInConstant(&(*pVm));
42422 	/* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
42423 	jx9RegisterBuiltInFunction(&(*pVm));
42424 	/* VM is ready for bytecode execution */
42425 	return SXRET_OK;
42426 }
42427 /*
42428  * Reset a Virtual Machine to it's initial state.
42429  */
jx9VmReset(jx9_vm * pVm)42430 JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
42431 {
42432 	if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
42433 		return SXERR_CORRUPT;
42434 	}
42435 	/* TICKET 1433-003: As of this version, the VM is automatically reset */
42436 	SyBlobReset(&pVm->sConsumer);
42437 	jx9MemObjRelease(&pVm->sExec);
42438 	/* Set the ready flag */
42439 	pVm->nMagic = JX9_VM_RUN;
42440 	return SXRET_OK;
42441 }
42442 /*
42443  * Release a Virtual Machine.
42444  * Every virtual machine must be destroyed in order to avoid memory leaks.
42445  */
jx9VmRelease(jx9_vm * pVm)42446 JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
42447 {
42448 	/* Set the stale magic number */
42449 	pVm->nMagic = JX9_VM_STALE;
42450 	/* Release the private memory subsystem */
42451 	SyMemBackendRelease(&pVm->sAllocator);
42452 	return SXRET_OK;
42453 }
42454 /*
42455  * Initialize a foreign function call context.
42456  * The context in which a foreign function executes is stored in a jx9_context object.
42457  * A pointer to a jx9_context object is always first parameter to application-defined foreign
42458  * functions.
42459  * The application-defined foreign function implementation will pass this pointer through into
42460  * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(),
42461  * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
42462  * and many more. Refer to the C/C++ Interfaces documentation for additional information.
42463  */
VmInitCallContext(jx9_context * pOut,jx9_vm * pVm,jx9_user_func * pFunc,jx9_value * pRet,sxi32 iFlags)42464 static sxi32 VmInitCallContext(
42465 	jx9_context *pOut,    /* Call Context */
42466 	jx9_vm *pVm,          /* Target VM */
42467 	jx9_user_func *pFunc, /* Foreign function to execute shortly */
42468 	jx9_value *pRet,      /* Store return value here*/
42469 	sxi32 iFlags          /* Control flags */
42470 	)
42471 {
42472 	pOut->pFunc = pFunc;
42473 	pOut->pVm   = pVm;
42474 	SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
42475 	SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data));
42476 	/* Assume a null return value */
42477 	MemObjSetType(pRet, MEMOBJ_NULL);
42478 	pOut->pRet = pRet;
42479 	pOut->iFlags = iFlags;
42480 	return SXRET_OK;
42481 }
42482 /*
42483  * Release a foreign function call context and cleanup the mess
42484  * left behind.
42485  */
VmReleaseCallContext(jx9_context * pCtx)42486 static void VmReleaseCallContext(jx9_context *pCtx)
42487 {
42488 	sxu32 n;
42489 	if( SySetUsed(&pCtx->sVar) > 0 ){
42490 		jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
42491 		for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
42492 			if( apObj[n] == 0 ){
42493 				/* Already released */
42494 				continue;
42495 			}
42496 			jx9MemObjRelease(apObj[n]);
42497 			SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
42498 		}
42499 		SySetRelease(&pCtx->sVar);
42500 	}
42501 	if( SySetUsed(&pCtx->sChunk) > 0 ){
42502 		jx9_aux_data *aAux;
42503 		void *pChunk;
42504 		/* Automatic release of dynamically allocated chunk
42505 		 * using [jx9_context_alloc_chunk()].
42506 		 */
42507 		aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
42508 		for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
42509 			pChunk = aAux[n].pAuxData;
42510 			/* Release the chunk */
42511 			if( pChunk ){
42512 				SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
42513 			}
42514 		}
42515 		SySetRelease(&pCtx->sChunk);
42516 	}
42517 }
42518 /*
42519  * Release a jx9_value allocated from the body of a foreign function.
42520  * Refer to [jx9_context_release_value()] for additional information.
42521  */
jx9VmReleaseContextValue(jx9_context * pCtx,jx9_value * pValue)42522 JX9_PRIVATE void jx9VmReleaseContextValue(
42523 	jx9_context *pCtx, /* Call context */
42524 	jx9_value *pValue  /* Release this value */
42525 	)
42526 {
42527 	if( pValue == 0 ){
42528 		/* NULL value is a harmless operation */
42529 		return;
42530 	}
42531 	if( SySetUsed(&pCtx->sVar) > 0 ){
42532 		jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
42533 		sxu32 n;
42534 		for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
42535 			if( apObj[n] == pValue ){
42536 				jx9MemObjRelease(pValue);
42537 				SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue);
42538 				/* Mark as released */
42539 				apObj[n] = 0;
42540 				break;
42541 			}
42542 		}
42543 	}
42544 }
42545 /*
42546  * Pop and release as many memory object from the operand stack.
42547  */
VmPopOperand(jx9_value ** ppTos,sxi32 nPop)42548 static void VmPopOperand(
42549 	jx9_value **ppTos, /* Operand stack */
42550 	sxi32 nPop         /* Total number of memory objects to pop */
42551 	)
42552 {
42553 	jx9_value *pTos = *ppTos;
42554 	while( nPop > 0 ){
42555 		jx9MemObjRelease(pTos);
42556 		pTos--;
42557 		nPop--;
42558 	}
42559 	/* Top of the stack */
42560 	*ppTos = pTos;
42561 }
42562 /*
42563  * Reserve a memory object.
42564  * Return a pointer to the raw jx9_value on success. NULL on failure.
42565  */
jx9VmReserveMemObj(jx9_vm * pVm,sxu32 * pIdx)42566 JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
42567 {
42568 	jx9_value *pObj = 0;
42569 	VmSlot *pSlot;
42570 	sxu32 nIdx;
42571 	/* Check for a free slot */
42572 	nIdx = SXU32_HIGH; /* cc warning */
42573 	pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj);
42574 	if( pSlot ){
42575 		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx);
42576 		nIdx = pSlot->nIdx;
42577 	}
42578 	if( pObj == 0 ){
42579 		/* Reserve a new memory object */
42580 		pObj = VmReserveMemObj(&(*pVm), &nIdx);
42581 		if( pObj == 0 ){
42582 			return 0;
42583 		}
42584 	}
42585 	/* Set a null default value */
42586 	jx9MemObjInit(&(*pVm), pObj);
42587 	if( pIdx ){
42588 		*pIdx = nIdx;
42589 	}
42590 	pObj->nIdx = nIdx;
42591 	return pObj;
42592 }
42593 /*
42594  * Extract a variable value from the top active VM frame.
42595  * Return a pointer to the variable value on success.
42596  * NULL otherwise (non-existent variable/Out-of-memory, ...).
42597  */
VmExtractMemObj(jx9_vm * pVm,const SyString * pName,int bDup,int bCreate)42598 static jx9_value * VmExtractMemObj(
42599 	jx9_vm *pVm,           /* Target VM */
42600 	const SyString *pName, /* Variable name */
42601 	int bDup,              /* True to duplicate variable name */
42602 	int bCreate            /* True to create the variable if non-existent */
42603 	)
42604 {
42605 	int bNullify = FALSE;
42606 	SyHashEntry *pEntry;
42607 	VmFrame *pFrame;
42608 	jx9_value *pObj;
42609 	sxu32 nIdx;
42610 	sxi32 rc;
42611 	/* Point to the top active frame */
42612 	pFrame = pVm->pFrame;
42613 	/* Perform the lookup */
42614 	if( pName == 0 || pName->nByte < 1 ){
42615 		static const SyString sAnnon = { " " , sizeof(char) };
42616 		pName = &sAnnon;
42617 		/* Always nullify the object */
42618 		bNullify = TRUE;
42619 		bDup = FALSE;
42620 	}
42621 	/* Check the superglobals table first */
42622 	pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte);
42623 	if( pEntry == 0 ){
42624 		/* Query the top active frame */
42625 		pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
42626 		if( pEntry == 0 ){
42627 			char *zName = (char *)pName->zString;
42628 			VmSlot sLocal;
42629 			if( !bCreate ){
42630 				/* Do not create the variable, return NULL */
42631 				return 0;
42632 			}
42633 			/* No such variable, automatically create a new one and install
42634 			 * it in the current frame.
42635 			 */
42636 			pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
42637 			if( pObj == 0 ){
42638 				return 0;
42639 			}
42640 			if( bDup ){
42641 				/* Duplicate name */
42642 				zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
42643 				if( zName == 0 ){
42644 					return 0;
42645 				}
42646 			}
42647 			/* Link to the top active VM frame */
42648 			rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx));
42649 			if( rc != SXRET_OK ){
42650 				/* Return the slot to the free pool */
42651 				sLocal.nIdx = nIdx;
42652 				sLocal.pUserData = 0;
42653 				SySetPut(&pVm->aFreeObj, (const void *)&sLocal);
42654 				return 0;
42655 			}
42656 			if( pFrame->pParent != 0 ){
42657 				/* Local variable */
42658 				sLocal.nIdx = nIdx;
42659 				SySetPut(&pFrame->sLocal, (const void *)&sLocal);
42660 			}
42661 		}else{
42662 			/* Extract variable contents */
42663 			nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
42664 			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42665 			if( bNullify && pObj ){
42666 				jx9MemObjRelease(pObj);
42667 			}
42668 		}
42669 	}else{
42670 		/* Superglobal */
42671 		nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
42672 		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42673 	}
42674 	return pObj;
42675 }
42676 /*
42677  * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, ....
42678  * Return a pointer to the variable value on success.NULL otherwise.
42679  */
VmExtractSuper(jx9_vm * pVm,const char * zName,sxu32 nByte)42680 static jx9_value * VmExtractSuper(
42681 	jx9_vm *pVm,       /* Target VM */
42682 	const char *zName, /* Superglobal name: NOT NULL TERMINATED */
42683 	sxu32 nByte        /* zName length */
42684 	)
42685 {
42686 	SyHashEntry *pEntry;
42687 	jx9_value *pValue;
42688 	sxu32 nIdx;
42689 	/* Query the superglobal table */
42690 	pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
42691 	if( pEntry == 0 ){
42692 		/* No such entry */
42693 		return 0;
42694 	}
42695 	/* Extract the superglobal index in the global object pool */
42696 	nIdx = SX_PTR_TO_INT(pEntry->pUserData);
42697 	/* Extract the variable value  */
42698 	pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42699 	return pValue;
42700 }
42701 /*
42702  * Perform a raw hashmap insertion.
42703  * Refer to the [jx9VmConfigure()] implementation for additional information.
42704  */
VmHashmapInsert(jx9_hashmap * pMap,const char * zKey,int nKeylen,const char * zData,int nLen)42705 static sxi32 VmHashmapInsert(
42706 	jx9_hashmap *pMap,  /* Target hashmap  */
42707 	const char *zKey,   /* Entry key */
42708 	int nKeylen,        /* zKey length*/
42709 	const char *zData,  /* Entry data */
42710 	int nLen            /* zData length */
42711 	)
42712 {
42713 	jx9_value sKey,sValue;
42714 	jx9_value *pKey;
42715 	sxi32 rc;
42716 	pKey = 0;
42717 	jx9MemObjInit(pMap->pVm, &sKey);
42718 	jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
42719 	if( zKey ){
42720 		if( nKeylen < 0 ){
42721 			nKeylen = (int)SyStrlen(zKey);
42722 		}
42723 		jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
42724 		pKey = &sKey;
42725 	}
42726 	if( zData ){
42727 		if( nLen < 0 ){
42728 			/* Compute length automatically */
42729 			nLen = (int)SyStrlen(zData);
42730 		}
42731 		jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
42732 	}
42733 	/* Perform the insertion */
42734 	rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
42735 	jx9MemObjRelease(&sKey);
42736 	jx9MemObjRelease(&sValue);
42737 	return rc;
42738 }
42739 /* Forward declaration */
42740 static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
42741 /*
42742  * Configure a working virtual machine instance.
42743  *
42744  * This routine is used to configure a JX9 virtual machine obtained by a prior
42745  * successful call to one of the compile interface such as jx9_compile()
42746  * jx9_compile_v2() or jx9_compile_file().
42747  * The second argument to this function is an integer configuration option
42748  * that determines what property of the JX9 virtual machine is to be configured.
42749  * Subsequent arguments vary depending on the configuration option in the second
42750  * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT,
42751  * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
42752  * Refer to the official documentation for the list of allowed verbs.
42753  */
jx9VmConfigure(jx9_vm * pVm,sxi32 nOp,va_list ap)42754 JX9_PRIVATE sxi32 jx9VmConfigure(
42755 	jx9_vm *pVm, /* Target VM */
42756 	sxi32 nOp,   /* Configuration verb */
42757 	va_list ap   /* Subsequent option arguments */
42758 	)
42759 {
42760 	sxi32 rc = SXRET_OK;
42761 	switch(nOp){
42762 	case JX9_VM_CONFIG_OUTPUT: {
42763 		ProcConsumer xConsumer = va_arg(ap, ProcConsumer);
42764 		void *pUserData = va_arg(ap, void *);
42765 		/* VM output consumer callback */
42766 #ifdef UNTRUST
42767 		if( xConsumer == 0 ){
42768 			rc = SXERR_CORRUPT;
42769 			break;
42770 		}
42771 #endif
42772 		/* Install the output consumer */
42773 		pVm->sVmConsumer.xConsumer = xConsumer;
42774 		pVm->sVmConsumer.pUserData = pUserData;
42775 		break;
42776 							   }
42777 	case JX9_VM_CONFIG_IMPORT_PATH: {
42778 		/* Import path */
42779 		  const char *zPath;
42780 		  SyString sPath;
42781 		  zPath = va_arg(ap, const char *);
42782 #if defined(UNTRUST)
42783 		  if( zPath == 0 ){
42784 			  rc = SXERR_EMPTY;
42785 			  break;
42786 		  }
42787 #endif
42788 		  SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath));
42789 		  /* Remove trailing slashes and backslashes */
42790 #ifdef __WINNT__
42791 		  SyStringTrimTrailingChar(&sPath, '\\');
42792 #endif
42793 		  SyStringTrimTrailingChar(&sPath, '/');
42794 		  /* Remove leading and trailing white spaces */
42795 		  SyStringFullTrim(&sPath);
42796 		  if( sPath.nByte > 0 ){
42797 			  /* Store the path in the corresponding conatiner */
42798 			  rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
42799 		  }
42800 		  break;
42801 									 }
42802 	case JX9_VM_CONFIG_ERR_REPORT:
42803 		/* Run-Time Error report */
42804 		pVm->bErrReport = 1;
42805 		break;
42806 	case JX9_VM_CONFIG_RECURSION_DEPTH:{
42807 		/* Recursion depth */
42808 		int nDepth = va_arg(ap, int);
42809 		if( nDepth > 2 && nDepth < 1024 ){
42810 			pVm->nMaxDepth = nDepth;
42811 		}
42812 		break;
42813 									   }
42814 	case JX9_VM_OUTPUT_LENGTH: {
42815 		/* VM output length in bytes */
42816 		sxu32 *pOut = va_arg(ap, sxu32 *);
42817 #ifdef UNTRUST
42818 		if( pOut == 0 ){
42819 			rc = SXERR_CORRUPT;
42820 			break;
42821 		}
42822 #endif
42823 		*pOut = pVm->nOutputLen;
42824 		break;
42825 							   }
42826 	case JX9_VM_CONFIG_CREATE_VAR: {
42827 		/* Create a new superglobal/global variable */
42828 		const char *zName = va_arg(ap, const char *);
42829 		jx9_value *pValue = va_arg(ap, jx9_value *);
42830 		SyHashEntry *pEntry;
42831 		jx9_value *pObj;
42832 		sxu32 nByte;
42833 		sxu32 nIdx;
42834 #ifdef UNTRUST
42835 		if( SX_EMPTY_STR(zName) || pValue == 0 ){
42836 			rc = SXERR_CORRUPT;
42837 			break;
42838 		}
42839 #endif
42840 		nByte = SyStrlen(zName);
42841 		/* Check if the superglobal is already installed */
42842 		pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
42843 		if( pEntry ){
42844 			/* Variable already installed */
42845 			nIdx = SX_PTR_TO_INT(pEntry->pUserData);
42846 			/* Extract contents */
42847 			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
42848 			if( pObj ){
42849 				/* Overwrite old contents */
42850 				jx9MemObjStore(pValue, pObj);
42851 			}
42852 		}else{
42853 			/* Install a new variable */
42854 			pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
42855 			if( pObj == 0 ){
42856 				rc = SXERR_MEM;
42857 				break;
42858 			}
42859 			/* Copy value */
42860 			jx9MemObjStore(pValue, pObj);
42861 			/* Install the superglobal */
42862 			rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
42863 		}
42864 		break;
42865 									}
42866 	case JX9_VM_CONFIG_SERVER_ATTR:
42867 	case JX9_VM_CONFIG_ENV_ATTR:  {
42868 		const char *zKey   = va_arg(ap, const char *);
42869 		const char *zValue = va_arg(ap, const char *);
42870 		int nLen = va_arg(ap, int);
42871 		jx9_hashmap *pMap;
42872 		jx9_value *pValue;
42873 		if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
42874 			/* Extract the $_ENV superglobal */
42875 			pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1);
42876 		}else{
42877 			/* Extract the $_SERVER superglobal */
42878 			pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1);
42879 		}
42880 		if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
42881 			/* No such entry */
42882 			rc = SXERR_NOTFOUND;
42883 			break;
42884 		}
42885 		/* Point to the hashmap */
42886 		pMap = (jx9_hashmap *)pValue->x.pOther;
42887 		/* Perform the insertion */
42888 		rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
42889 		break;
42890 								   }
42891 	case JX9_VM_CONFIG_ARGV_ENTRY:{
42892 		/* Script arguments */
42893 		const char *zValue = va_arg(ap, const char *);
42894 		jx9_hashmap *pMap;
42895 		jx9_value *pValue;
42896 		/* Extract the $argv array */
42897 		pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1);
42898 		if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
42899 			/* No such entry */
42900 			rc = SXERR_NOTFOUND;
42901 			break;
42902 		}
42903 		/* Point to the hashmap */
42904 		pMap = (jx9_hashmap *)pValue->x.pOther;
42905 		/* Perform the insertion */
42906 		rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
42907 		if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
42908 			if( pMap->nEntry > 1 ){
42909 				/* Append space separator first */
42910 				SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
42911 			}
42912 			SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
42913 		}
42914 		break;
42915 								  }
42916 	case JX9_VM_CONFIG_EXEC_VALUE: {
42917 		/* Script return value */
42918 		jx9_value **ppValue = va_arg(ap, jx9_value **);
42919 #ifdef UNTRUST
42920 		if( ppValue == 0 ){
42921 			rc = SXERR_CORRUPT;
42922 			break;
42923 		}
42924 #endif
42925 		*ppValue = &pVm->sExec;
42926 		break;
42927 								   }
42928 	case JX9_VM_CONFIG_IO_STREAM: {
42929 		/* Register an IO stream device */
42930 		const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *);
42931 		/* Make sure we are dealing with a valid IO stream */
42932 		if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 ||
42933 			pStream->xOpen == 0 || pStream->xRead == 0 ){
42934 				/* Invalid stream */
42935 				rc = SXERR_INVALID;
42936 				break;
42937 		}
42938 		if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){
42939 			/* Make the 'file://' stream the defaut stream device */
42940 			pVm->pDefStream = pStream;
42941 		}
42942 		/* Insert in the appropriate container */
42943 		rc = SySetPut(&pVm->aIOstream, (const void *)&pStream);
42944 		break;
42945 								  }
42946 	case JX9_VM_CONFIG_EXTRACT_OUTPUT: {
42947 		/* Point to the VM internal output consumer buffer */
42948 		const void **ppOut = va_arg(ap, const void **);
42949 		unsigned int *pLen = va_arg(ap, unsigned int *);
42950 #ifdef UNTRUST
42951 		if( ppOut == 0 || pLen == 0 ){
42952 			rc = SXERR_CORRUPT;
42953 			break;
42954 		}
42955 #endif
42956 		*ppOut = SyBlobData(&pVm->sConsumer);
42957 		*pLen  = SyBlobLength(&pVm->sConsumer);
42958 		break;
42959 									   }
42960 	case JX9_VM_CONFIG_HTTP_REQUEST:{
42961 		/* Raw HTTP request*/
42962 		const char *zRequest = va_arg(ap, const char *);
42963 		int nByte = va_arg(ap, int);
42964 		if( SX_EMPTY_STR(zRequest) ){
42965 			rc = SXERR_EMPTY;
42966 			break;
42967 		}
42968 		if( nByte < 0 ){
42969 			/* Compute length automatically */
42970 			nByte = (int)SyStrlen(zRequest);
42971 		}
42972 		/* Process the request */
42973 		rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte);
42974 		break;
42975 									}
42976 	default:
42977 		/* Unknown configuration option */
42978 		rc = SXERR_UNKNOWN;
42979 		break;
42980 	}
42981 	return rc;
42982 }
42983 /* Forward declaration */
42984 static const char * VmInstrToString(sxi32 nOp);
42985 /*
42986  * This routine is used to dump JX9 bytecode instructions to a human readable
42987  * format.
42988  * The dump is redirected to the given consumer callback which is responsible
42989  * of consuming the generated dump perhaps redirecting it to its standard output
42990  * (STDOUT).
42991  */
VmByteCodeDump(SySet * pByteCode,ProcConsumer xConsumer,void * pUserData)42992 static sxi32 VmByteCodeDump(
42993 	SySet *pByteCode,       /* Bytecode container */
42994 	ProcConsumer xConsumer, /* Dump consumer callback */
42995 	void *pUserData         /* Last argument to xConsumer() */
42996 	)
42997 {
42998 	static const char zDump[] = {
42999 		"====================================================\n"
43000 		"JX9 VM Dump   Copyright (C) 2012-2013 Symisc Systems\n"
43001 		"                              http://jx9.symisc.net/\n"
43002 		"====================================================\n"
43003 	};
43004 	VmInstr *pInstr, *pEnd;
43005 	sxi32 rc = SXRET_OK;
43006 	sxu32 n;
43007 	/* Point to the JX9 instructions */
43008 	pInstr = (VmInstr *)SySetBasePtr(pByteCode);
43009 	pEnd   = &pInstr[SySetUsed(pByteCode)];
43010 	n = 0;
43011 	xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData);
43012 	/* Dump instructions */
43013 	for(;;){
43014 		if( pInstr >= pEnd ){
43015 			/* No more instructions */
43016 			break;
43017 		}
43018 		/* Format and call the consumer callback */
43019 		rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n",
43020 			VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
43021 			SX_PTR_TO_INT(pInstr->p3), n);
43022 		if( rc != SXRET_OK ){
43023 			/* Consumer routine request an operation abort */
43024 			return rc;
43025 		}
43026 		++n;
43027 		pInstr++; /* Next instruction in the stream */
43028 	}
43029 	return rc;
43030 }
43031 /*
43032  * Consume a generated run-time error message by invoking the VM output
43033  * consumer callback.
43034  */
VmCallErrorHandler(jx9_vm * pVm,SyBlob * pMsg)43035 static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
43036 {
43037 	jx9_output_consumer *pCons = &pVm->sVmConsumer;
43038 	sxi32 rc = SXRET_OK;
43039 	/* Append a new line */
43040 #ifdef __WINNT__
43041 	SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1);
43042 #else
43043 	SyBlobAppend(pMsg, "\n", sizeof(char));
43044 #endif
43045 	/* Invoke the output consumer callback */
43046 	rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData);
43047 	/* Increment output length */
43048 	pVm->nOutputLen += SyBlobLength(pMsg);
43049 
43050 	return rc;
43051 }
43052 /*
43053  * Throw a run-time error and invoke the supplied VM output consumer callback.
43054  * Refer to the implementation of [jx9_context_throw_error()] for additional
43055  * information.
43056  */
jx9VmThrowError(jx9_vm * pVm,SyString * pFuncName,sxi32 iErr,const char * zMessage)43057 JX9_PRIVATE sxi32 jx9VmThrowError(
43058 	jx9_vm *pVm,         /* Target VM */
43059 	SyString *pFuncName, /* Function name. NULL otherwise */
43060 	sxi32 iErr,          /* Severity level: [i.e: Error, Warning or Notice]*/
43061 	const char *zMessage /* Null terminated error message */
43062 	)
43063 {
43064 	SyBlob *pWorker = &pVm->sWorker;
43065 	SyString *pFile;
43066 	char *zErr;
43067 	sxi32 rc;
43068 	if( !pVm->bErrReport ){
43069 		/* Don't bother reporting errors */
43070 		return SXRET_OK;
43071 	}
43072 	/* Reset the working buffer */
43073 	SyBlobReset(pWorker);
43074 	/* Peek the processed file if available */
43075 	pFile = (SyString *)SySetPeek(&pVm->aFiles);
43076 	if( pFile ){
43077 		/* Append file name */
43078 		SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
43079 		SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
43080 	}
43081 	zErr = "Error: ";
43082 	switch(iErr){
43083 	case JX9_CTX_WARNING: zErr = "Warning: "; break;
43084 	case JX9_CTX_NOTICE:  zErr = "Notice: ";  break;
43085 	default:
43086 		iErr = JX9_CTX_ERR;
43087 		break;
43088 	}
43089 	SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
43090 	if( pFuncName ){
43091 		/* Append function name first */
43092 		SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
43093 		SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
43094 	}
43095 	SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage));
43096 	/* Consume the error message */
43097 	rc = VmCallErrorHandler(&(*pVm), pWorker);
43098 	return rc;
43099 }
43100 /*
43101  * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43102  * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43103  * information.
43104  */
VmThrowErrorAp(jx9_vm * pVm,SyString * pFuncName,sxi32 iErr,const char * zFormat,va_list ap)43105 static sxi32 VmThrowErrorAp(
43106 	jx9_vm *pVm,         /* Target VM */
43107 	SyString *pFuncName, /* Function name. NULL otherwise */
43108 	sxi32 iErr,          /* Severity level: [i.e: Error, Warning or Notice] */
43109 	const char *zFormat, /* Format message */
43110 	va_list ap           /* Variable list of arguments */
43111 	)
43112 {
43113 	SyBlob *pWorker = &pVm->sWorker;
43114 	SyString *pFile;
43115 	char *zErr;
43116 	sxi32 rc;
43117 	if( !pVm->bErrReport ){
43118 		/* Don't bother reporting errors */
43119 		return SXRET_OK;
43120 	}
43121 	/* Reset the working buffer */
43122 	SyBlobReset(pWorker);
43123 	/* Peek the processed file if available */
43124 	pFile = (SyString *)SySetPeek(&pVm->aFiles);
43125 	if( pFile ){
43126 		/* Append file name */
43127 		SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
43128 		SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
43129 	}
43130 	zErr = "Error: ";
43131 	switch(iErr){
43132 	case JX9_CTX_WARNING: zErr = "Warning: "; break;
43133 	case JX9_CTX_NOTICE:  zErr = "Notice: ";  break;
43134 	default:
43135 		iErr = JX9_CTX_ERR;
43136 		break;
43137 	}
43138 	SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
43139 	if( pFuncName ){
43140 		/* Append function name first */
43141 		SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
43142 		SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
43143 	}
43144 	SyBlobFormatAp(pWorker, zFormat, ap);
43145 	/* Consume the error message */
43146 	rc = VmCallErrorHandler(&(*pVm), pWorker);
43147 	return rc;
43148 }
43149 /*
43150  * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43151  * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43152  * information.
43153  * ------------------------------------
43154  * Simple boring wrapper function.
43155  * ------------------------------------
43156  */
VmErrorFormat(jx9_vm * pVm,sxi32 iErr,const char * zFormat,...)43157 static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...)
43158 {
43159 	va_list ap;
43160 	sxi32 rc;
43161 	va_start(ap, zFormat);
43162 	rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap);
43163 	va_end(ap);
43164 	return rc;
43165 }
43166 /*
43167  * Format and throw a run-time error and invoke the supplied VM output consumer callback.
43168  * Refer to the implementation of [jx9_context_throw_error_format()] for additional
43169  * information.
43170  * ------------------------------------
43171  * Simple boring wrapper function.
43172  * ------------------------------------
43173  */
jx9VmThrowErrorAp(jx9_vm * pVm,SyString * pFuncName,sxi32 iErr,const char * zFormat,va_list ap)43174 JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
43175 {
43176 	sxi32 rc;
43177 	rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
43178 	return rc;
43179 }
43180 /* Forward declaration */
43181 static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
43182 /*
43183  * Execute as much of a JX9 bytecode program as we can then return.
43184  *
43185  * [jx9VmMakeReady()] must be called before this routine in order to
43186  * close the program with a final OP_DONE and to set up the default
43187  * consumer routines and other stuff. Refer to the implementation
43188  * of [jx9VmMakeReady()] for additional information.
43189  * If the installed VM output consumer callback ever returns JX9_ABORT
43190  * then the program execution is halted.
43191  * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
43192  * should be used respectively to clean up the mess that was left behind
43193  * or to reset the VM to it's initial state.
43194  */
VmByteCodeExec(jx9_vm * pVm,VmInstr * aInstr,jx9_value * pStack,int nTos,jx9_value * pResult)43195 static sxi32 VmByteCodeExec(
43196 	jx9_vm *pVm,         /* Target VM */
43197 	VmInstr *aInstr,     /* JX9 bytecode program */
43198 	jx9_value *pStack,   /* Operand stack */
43199 	int nTos,            /* Top entry in the operand stack (usually -1) */
43200 	jx9_value *pResult  /* Store program return value here. NULL otherwise */
43201 	)
43202 {
43203 	VmInstr *pInstr;
43204 	jx9_value *pTos;
43205 	SySet aArg;
43206 	sxi32 pc;
43207 	sxi32 rc;
43208 	/* Argument container */
43209 	SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
43210 	if( nTos < 0 ){
43211 		pTos = &pStack[-1];
43212 	}else{
43213 		pTos = &pStack[nTos];
43214 	}
43215 	pc = 0;
43216 	/* Execute as much as we can */
43217 	for(;;){
43218 		/* Fetch the instruction to execute */
43219 		pInstr = &aInstr[pc];
43220 		rc = SXRET_OK;
43221 /*
43222  * What follows here is a massive switch statement where each case implements a
43223  * separate instruction in the virtual machine.  If we follow the usual
43224  * indentation convention each case should be indented by 6 spaces.  But
43225  * that is a lot of wasted space on the left margin.  So the code within
43226  * the switch statement will break with convention and be flush-left.
43227  */
43228 		switch(pInstr->iOp){
43229 /*
43230  * DONE: P1 * *
43231  *
43232  * Program execution completed: Clean up the mess left behind
43233  * and return immediately.
43234  */
43235 case JX9_OP_DONE:
43236 	if( pInstr->iP1 ){
43237 #ifdef UNTRUST
43238 		if( pTos < pStack ){
43239 			goto Abort;
43240 		}
43241 #endif
43242 		if( pResult ){
43243 			/* Execution result */
43244 			jx9MemObjStore(pTos, pResult);
43245 		}
43246 		VmPopOperand(&pTos, 1);
43247 	}
43248 	goto Done;
43249 /*
43250  * HALT: P1 * *
43251  *
43252  * Program execution aborted: Clean up the mess left behind
43253  * and abort immediately.
43254  */
43255 case JX9_OP_HALT:
43256 	if( pInstr->iP1 ){
43257 #ifdef UNTRUST
43258 		if( pTos < pStack ){
43259 			goto Abort;
43260 		}
43261 #endif
43262 		if( pTos->iFlags & MEMOBJ_STRING ){
43263 			if( SyBlobLength(&pTos->sBlob) > 0 ){
43264 				/* Output the exit message */
43265 				pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),
43266 					pVm->sVmConsumer.pUserData);
43267 					/* Increment output length */
43268 					pVm->nOutputLen += SyBlobLength(&pTos->sBlob);
43269 			}
43270 		}else if(pTos->iFlags & MEMOBJ_INT ){
43271 			/* Record exit status */
43272 			pVm->iExitStatus = (sxi32)pTos->x.iVal;
43273 		}
43274 		VmPopOperand(&pTos, 1);
43275 	}
43276 	goto Abort;
43277 /*
43278  * JMP: * P2 *
43279  *
43280  * Unconditional jump: The next instruction executed will be
43281  * the one at index P2 from the beginning of the program.
43282  */
43283 case JX9_OP_JMP:
43284 	pc = pInstr->iP2 - 1;
43285 	break;
43286 /*
43287  * JZ: P1 P2 *
43288  *
43289  * Take the jump if the top value is zero (FALSE jump).Pop the top most
43290  * entry in the stack if P1 is zero.
43291  */
43292 case JX9_OP_JZ:
43293 #ifdef UNTRUST
43294 	if( pTos < pStack ){
43295 		goto Abort;
43296 	}
43297 #endif
43298 	/* Get a boolean value */
43299 	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43300 		jx9MemObjToBool(pTos);
43301 	}
43302 	if( !pTos->x.iVal ){
43303 		/* Take the jump */
43304 		pc = pInstr->iP2 - 1;
43305 	}
43306 	if( !pInstr->iP1 ){
43307 		VmPopOperand(&pTos, 1);
43308 	}
43309 	break;
43310 /*
43311  * JNZ: P1 P2 *
43312  *
43313  * Take the jump if the top value is not zero (TRUE jump).Pop the top most
43314  * entry in the stack if P1 is zero.
43315  */
43316 case JX9_OP_JNZ:
43317 #ifdef UNTRUST
43318 	if( pTos < pStack ){
43319 		goto Abort;
43320 	}
43321 #endif
43322 	/* Get a boolean value */
43323 	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43324 		jx9MemObjToBool(pTos);
43325 	}
43326 	if( pTos->x.iVal ){
43327 		/* Take the jump */
43328 		pc = pInstr->iP2 - 1;
43329 	}
43330 	if( !pInstr->iP1 ){
43331 		VmPopOperand(&pTos, 1);
43332 	}
43333 	break;
43334 /*
43335  * NOOP: * * *
43336  *
43337  * Do nothing. This instruction is often useful as a jump
43338  * destination.
43339  */
43340 case JX9_OP_NOOP:
43341 	break;
43342 /*
43343  * POP: P1 * *
43344  *
43345  * Pop P1 elements from the operand stack.
43346  */
43347 case JX9_OP_POP: {
43348 	sxi32 n = pInstr->iP1;
43349 	if( &pTos[-n+1] < pStack ){
43350 		/* TICKET 1433-51 Stack underflow must be handled at run-time */
43351 		n = (sxi32)(pTos - pStack);
43352 	}
43353 	VmPopOperand(&pTos, n);
43354 	break;
43355 				 }
43356 /*
43357  * CVT_INT: * * *
43358  *
43359  * Force the top of the stack to be an integer.
43360  */
43361 case JX9_OP_CVT_INT:
43362 #ifdef UNTRUST
43363 	if( pTos < pStack ){
43364 		goto Abort;
43365 	}
43366 #endif
43367 	if((pTos->iFlags & MEMOBJ_INT) == 0 ){
43368 		jx9MemObjToInteger(pTos);
43369 	}
43370 	/* Invalidate any prior representation */
43371 	MemObjSetType(pTos, MEMOBJ_INT);
43372 	break;
43373 /*
43374  * CVT_REAL: * * *
43375  *
43376  * Force the top of the stack to be a real.
43377  */
43378 case JX9_OP_CVT_REAL:
43379 #ifdef UNTRUST
43380 	if( pTos < pStack ){
43381 		goto Abort;
43382 	}
43383 #endif
43384 	if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
43385 		jx9MemObjToReal(pTos);
43386 	}
43387 	/* Invalidate any prior representation */
43388 	MemObjSetType(pTos, MEMOBJ_REAL);
43389 	break;
43390 /*
43391  * CVT_STR: * * *
43392  *
43393  * Force the top of the stack to be a string.
43394  */
43395 case JX9_OP_CVT_STR:
43396 #ifdef UNTRUST
43397 	if( pTos < pStack ){
43398 		goto Abort;
43399 	}
43400 #endif
43401 	if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43402 		jx9MemObjToString(pTos);
43403 	}
43404 	break;
43405 /*
43406  * CVT_BOOL: * * *
43407  *
43408  * Force the top of the stack to be a boolean.
43409  */
43410 case JX9_OP_CVT_BOOL:
43411 #ifdef UNTRUST
43412 	if( pTos < pStack ){
43413 		goto Abort;
43414 	}
43415 #endif
43416 	if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
43417 		jx9MemObjToBool(pTos);
43418 	}
43419 	break;
43420 /*
43421  * CVT_NULL: * * *
43422  *
43423  * Nullify the top of the stack.
43424  */
43425 case JX9_OP_CVT_NULL:
43426 #ifdef UNTRUST
43427 	if( pTos < pStack ){
43428 		goto Abort;
43429 	}
43430 #endif
43431 	jx9MemObjRelease(pTos);
43432 	break;
43433 /*
43434  * CVT_NUMC: * * *
43435  *
43436  * Force the top of the stack to be a numeric type (integer, real or both).
43437  */
43438 case JX9_OP_CVT_NUMC:
43439 #ifdef UNTRUST
43440 	if( pTos < pStack ){
43441 		goto Abort;
43442 	}
43443 #endif
43444 	/* Force a numeric cast */
43445 	jx9MemObjToNumeric(pTos);
43446 	break;
43447 /*
43448  * CVT_ARRAY: * * *
43449  *
43450  * Force the top of the stack to be a hashmap aka 'array'.
43451  */
43452 case JX9_OP_CVT_ARRAY:
43453 #ifdef UNTRUST
43454 	if( pTos < pStack ){
43455 		goto Abort;
43456 	}
43457 #endif
43458 	/* Force a hashmap cast */
43459 	rc = jx9MemObjToHashmap(pTos);
43460 	if( rc != SXRET_OK ){
43461 		/* Not so fatal, emit a simple warning */
43462 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
43463 			"JX9 engine is running out of memory while performing an array cast");
43464 	}
43465 	break;
43466 /*
43467  * LOADC P1 P2 *
43468  *
43469  * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool.
43470  * If P1 is set, then this constant is candidate for expansion via user installable callbacks.
43471  */
43472 case JX9_OP_LOADC: {
43473 	jx9_value *pObj;
43474 	/* Reserve a room */
43475 	pTos++;
43476 	if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){
43477 		if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){
43478 			SyHashEntry *pEntry;
43479 			/* Candidate for expansion via user defined callbacks */
43480 			pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
43481 			if( pEntry ){
43482 				jx9_constant *pCons = (jx9_constant *)pEntry->pUserData;
43483 				/* Set a NULL default value */
43484 				MemObjSetType(pTos, MEMOBJ_NULL);
43485 				SyBlobReset(&pTos->sBlob);
43486 				/* Invoke the callback and deal with the expanded value */
43487 				pCons->xExpand(pTos, pCons->pUserData);
43488 				/* Mark as constant */
43489 				pTos->nIdx = SXU32_HIGH;
43490 				break;
43491 			}
43492 		}
43493 		jx9MemObjLoad(pObj, pTos);
43494 	}else{
43495 		/* Set a NULL value */
43496 		MemObjSetType(pTos, MEMOBJ_NULL);
43497 	}
43498 	/* Mark as constant */
43499 	pTos->nIdx = SXU32_HIGH;
43500 	break;
43501 				  }
43502 /*
43503  * LOAD: P1 * P3
43504  *
43505  * Load a variable where it's name is taken from the top of the stack or
43506  * from the P3 operand.
43507  * If P1 is set, then perform a lookup only.In other words do not create
43508  * the variable if non existent and push the NULL constant instead.
43509  */
43510 case JX9_OP_LOAD:{
43511 	jx9_value *pObj;
43512 	SyString sName;
43513 	if( pInstr->p3 == 0 ){
43514 		/* Take the variable name from the top of the stack */
43515 #ifdef UNTRUST
43516 		if( pTos < pStack ){
43517 			goto Abort;
43518 		}
43519 #endif
43520 		/* Force a string cast */
43521 		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43522 			jx9MemObjToString(pTos);
43523 		}
43524 		SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43525 	}else{
43526 		SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
43527 		/* Reserve a room for the target object */
43528 		pTos++;
43529 	}
43530 	/* Extract the requested memory object */
43531 	pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1);
43532 	if( pObj == 0 ){
43533 		if( pInstr->iP1 ){
43534 			/* Variable not found, load NULL */
43535 			if( !pInstr->p3 ){
43536 				jx9MemObjRelease(pTos);
43537 			}else{
43538 				MemObjSetType(pTos, MEMOBJ_NULL);
43539 			}
43540 			pTos->nIdx = SXU32_HIGH; /* Mark as constant */
43541 			break;
43542 		}else{
43543 			/* Fatal error */
43544 			VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
43545 			goto Abort;
43546 		}
43547 	}
43548 	/* Load variable contents */
43549 	jx9MemObjLoad(pObj, pTos);
43550 	pTos->nIdx = pObj->nIdx;
43551 	break;
43552 				   }
43553 /*
43554  * LOAD_MAP P1 * *
43555  *
43556  * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack.
43557  * If the P1 operand is greater than zero then pop P1 elements from the
43558  * stack and insert them (key => value pair) in the new hashmap.
43559  */
43560 case JX9_OP_LOAD_MAP: {
43561 	jx9_hashmap *pMap;
43562 	int is_json_object; /* TRUE if we are dealing with a JSON object */
43563 	int iIncr = 1;
43564 	/* Allocate a new hashmap instance */
43565 	pMap = jx9NewHashmap(&(*pVm), 0, 0);
43566 	if( pMap == 0 ){
43567 		VmErrorFormat(&(*pVm), JX9_CTX_ERR,
43568 			"Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
43569 		goto Abort;
43570 	}
43571 	is_json_object = 0;
43572 	if( pInstr->iP2 ){
43573 		/* JSON object, record that */
43574 		pMap->iFlags |= HASHMAP_JSON_OBJECT;
43575 		is_json_object = 1;
43576 		iIncr = 2;
43577 	}
43578 	if( pInstr->iP1 > 0 ){
43579 		jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
43580 		/* Perform the insertion */
43581 		while( pEntry <= pTos ){
43582 			/* Standard insertion */
43583 			jx9HashmapInsert(pMap,
43584 				is_json_object ? pEntry : 0 /* Automatic index assign */,
43585 				is_json_object ? &pEntry[1] : pEntry
43586 			);
43587 			/* Next pair on the stack */
43588 			pEntry += iIncr;
43589 		}
43590 		/* Pop P1 elements */
43591 		VmPopOperand(&pTos, pInstr->iP1);
43592 	}
43593 	/* Push the hashmap */
43594 	pTos++;
43595 	pTos->x.pOther = pMap;
43596 	MemObjSetType(pTos, MEMOBJ_HASHMAP);
43597 	break;
43598 					  }
43599 /*
43600  * LOAD_IDX: P1 P2 *
43601  *
43602  * Load a hasmap entry where it's index (either numeric or string) is taken
43603  * from the stack.
43604  * If the index does not refer to a valid element, then push the NULL constant
43605  * instead.
43606  */
43607 case JX9_OP_LOAD_IDX: {
43608 	jx9_hashmap_node *pNode = 0; /* cc warning */
43609 	jx9_hashmap *pMap = 0;
43610 	jx9_value *pIdx;
43611 	pIdx = 0;
43612 	if( pInstr->iP1 == 0 ){
43613 		if( !pInstr->iP2){
43614 			/* No available index, load NULL */
43615 			if( pTos >= pStack ){
43616 				jx9MemObjRelease(pTos);
43617 			}else{
43618 				/* TICKET 1433-020: Empty stack */
43619 				pTos++;
43620 				MemObjSetType(pTos, MEMOBJ_NULL);
43621 				pTos->nIdx = SXU32_HIGH;
43622 			}
43623 			/* Emit a notice */
43624 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE,
43625 				"JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL");
43626 			break;
43627 		}
43628 	}else{
43629 		pIdx = pTos;
43630 		pTos--;
43631 	}
43632 	if( pTos->iFlags & MEMOBJ_STRING ){
43633 		/* String access */
43634 		if( pIdx ){
43635 			sxu32 nOfft;
43636 			if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){
43637 				/* Force an int cast */
43638 				jx9MemObjToInteger(pIdx);
43639 			}
43640 			nOfft = (sxu32)pIdx->x.iVal;
43641 			if( nOfft >= SyBlobLength(&pTos->sBlob) ){
43642 				/* Invalid offset, load null */
43643 				jx9MemObjRelease(pTos);
43644 			}else{
43645 				const char *zData = (const char *)SyBlobData(&pTos->sBlob);
43646 				int c = zData[nOfft];
43647 				jx9MemObjRelease(pTos);
43648 				MemObjSetType(pTos, MEMOBJ_STRING);
43649 				SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char));
43650 			}
43651 		}else{
43652 			/* No available index, load NULL */
43653 			MemObjSetType(pTos, MEMOBJ_NULL);
43654 		}
43655 		break;
43656 	}
43657 	if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){
43658 		if( pTos->nIdx != SXU32_HIGH ){
43659 			jx9_value *pObj;
43660 			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
43661 				jx9MemObjToHashmap(pObj);
43662 				jx9MemObjLoad(pObj, pTos);
43663 			}
43664 		}
43665 	}
43666 	rc = SXERR_NOTFOUND; /* Assume the index is invalid */
43667 	if( pTos->iFlags & MEMOBJ_HASHMAP ){
43668 		/* Point to the hashmap */
43669 		pMap = (jx9_hashmap *)pTos->x.pOther;
43670 		if( pIdx ){
43671 			/* Load the desired entry */
43672 			rc = jx9HashmapLookup(pMap, pIdx, &pNode);
43673 		}
43674 		if( rc != SXRET_OK && pInstr->iP2 ){
43675 			/* Create a new empty entry */
43676 			rc = jx9HashmapInsert(pMap, pIdx, 0);
43677 			if( rc == SXRET_OK ){
43678 				/* Point to the last inserted entry */
43679 				pNode = pMap->pLast;
43680 			}
43681 		}
43682 	}
43683 	if( pIdx ){
43684 		jx9MemObjRelease(pIdx);
43685 	}
43686 	if( rc == SXRET_OK ){
43687 		/* Load entry contents */
43688 		if( pMap->iRef < 2 ){
43689 			/* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
43690 			 * of the entry value, rather than pointing to it.
43691 			 */
43692 			pTos->nIdx = SXU32_HIGH;
43693 			jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
43694 		}else{
43695 			pTos->nIdx = pNode->nValIdx;
43696 			jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
43697 			jx9HashmapUnref(pMap);
43698 		}
43699 	}else{
43700 		/* No such entry, load NULL */
43701 		jx9MemObjRelease(pTos);
43702 		pTos->nIdx = SXU32_HIGH;
43703 	}
43704 	break;
43705 					  }
43706 /*
43707  * STORE * P2 P3
43708  *
43709  * Perform a store (Assignment) operation.
43710  */
43711 case JX9_OP_STORE: {
43712 	jx9_value *pObj;
43713 	SyString sName;
43714 #ifdef UNTRUST
43715 	if( pTos < pStack ){
43716 		goto Abort;
43717 	}
43718 #endif
43719 	if( pInstr->iP2 ){
43720 		sxu32 nIdx;
43721 		/* Member store operation */
43722 		nIdx = pTos->nIdx;
43723 		VmPopOperand(&pTos, 1);
43724 		if( nIdx == SXU32_HIGH ){
43725 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
43726 				"Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
43727 			pTos->nIdx = SXU32_HIGH;
43728 		}else{
43729 			/* Point to the desired memory object */
43730 			pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
43731 			if( pObj ){
43732 				/* Perform the store operation */
43733 				jx9MemObjStore(pTos, pObj);
43734 			}
43735 		}
43736 		break;
43737 	}else if( pInstr->p3 == 0 ){
43738 		/* Take the variable name from the next on the stack */
43739 		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
43740 			/* Force a string cast */
43741 			jx9MemObjToString(pTos);
43742 		}
43743 		SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43744 		pTos--;
43745 #ifdef UNTRUST
43746 		if( pTos < pStack  ){
43747 			goto Abort;
43748 		}
43749 #endif
43750 	}else{
43751 		SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
43752 	}
43753 	/* Extract the desired variable and if not available dynamically create it */
43754 	pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE);
43755 	if( pObj == 0 ){
43756 		VmErrorFormat(&(*pVm), JX9_CTX_ERR,
43757 			"Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
43758 		goto Abort;
43759 	}
43760 	if( !pInstr->p3 ){
43761 		jx9MemObjRelease(&pTos[1]);
43762 	}
43763 	/* Perform the store operation */
43764 	jx9MemObjStore(pTos, pObj);
43765 	break;
43766 				   }
43767 /*
43768  * STORE_IDX:   P1 * P3
43769  *
43770  * Perfrom a store operation an a hashmap entry.
43771  */
43772 case JX9_OP_STORE_IDX: {
43773 	jx9_hashmap *pMap = 0; /* cc  warning */
43774 	jx9_value *pKey;
43775 	sxu32 nIdx;
43776 	if( pInstr->iP1 ){
43777 		/* Key is next on stack */
43778 		pKey = pTos;
43779 		pTos--;
43780 	}else{
43781 		pKey = 0;
43782 	}
43783 	nIdx = pTos->nIdx;
43784 	if( pTos->iFlags & MEMOBJ_HASHMAP ){
43785 		/* Hashmap already loaded */
43786 		pMap = (jx9_hashmap *)pTos->x.pOther;
43787 		if( pMap->iRef < 2 ){
43788 			/* TICKET 1433-48: Prevent garbage collection */
43789 			pMap->iRef = 2;
43790 		}
43791 	}else{
43792 		jx9_value *pObj;
43793 		pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
43794 		if( pObj == 0 ){
43795 			if( pKey ){
43796 			  jx9MemObjRelease(pKey);
43797 			}
43798 			VmPopOperand(&pTos, 1);
43799 			break;
43800 		}
43801 		/* Phase#1: Load the array */
43802 		if( (pObj->iFlags & MEMOBJ_STRING)  ){
43803 			VmPopOperand(&pTos, 1);
43804 			if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
43805 				/* Force a string cast */
43806 				jx9MemObjToString(pTos);
43807 			}
43808 			if( pKey == 0 ){
43809 				/* Append string */
43810 				if( SyBlobLength(&pTos->sBlob) > 0 ){
43811 					SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
43812 				}
43813 			}else{
43814 				sxu32 nOfft;
43815 				if((pKey->iFlags & MEMOBJ_INT)){
43816 					/* Force an int cast */
43817 					jx9MemObjToInteger(pKey);
43818 				}
43819 				nOfft = (sxu32)pKey->x.iVal;
43820 				if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){
43821 					const char *zBlob = (const char *)SyBlobData(&pTos->sBlob);
43822 					char *zData = (char *)SyBlobData(&pObj->sBlob);
43823 					zData[nOfft] = zBlob[0];
43824 				}else{
43825 					if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){
43826 						/* Perform an append operation */
43827 						SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char));
43828 					}
43829 				}
43830 			}
43831 			if( pKey ){
43832 			  jx9MemObjRelease(pKey);
43833 			}
43834 			break;
43835 		}else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
43836 			/* Force a hashmap cast  */
43837 			rc = jx9MemObjToHashmap(pObj);
43838 			if( rc != SXRET_OK ){
43839 				VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
43840 				goto Abort;
43841 			}
43842 		}
43843 		pMap = (jx9_hashmap *)pObj->x.pOther;
43844 	}
43845 	VmPopOperand(&pTos, 1);
43846 	/* Phase#2: Perform the insertion */
43847 	jx9HashmapInsert(pMap, pKey, pTos);
43848 	if( pKey ){
43849 		jx9MemObjRelease(pKey);
43850 	}
43851 	break;
43852 					   }
43853 /*
43854  * INCR: P1 * *
43855  *
43856  * Force a numeric cast and increment the top of the stack by 1.
43857  * If the P1 operand is set then perform a duplication of the top of
43858  * the stack and increment after that.
43859  */
43860 case JX9_OP_INCR:
43861 #ifdef UNTRUST
43862 	if( pTos < pStack ){
43863 		goto Abort;
43864 	}
43865 #endif
43866 	if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
43867 		if( pTos->nIdx != SXU32_HIGH ){
43868 			jx9_value *pObj;
43869 			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
43870 				/* Force a numeric cast */
43871 				jx9MemObjToNumeric(pObj);
43872 				if( pObj->iFlags & MEMOBJ_REAL ){
43873 					pObj->x.rVal++;
43874 					/* Try to get an integer representation */
43875 					jx9MemObjTryInteger(pTos);
43876 				}else{
43877 					pObj->x.iVal++;
43878 					MemObjSetType(pTos, MEMOBJ_INT);
43879 				}
43880 				if( pInstr->iP1 ){
43881 					/* Pre-icrement */
43882 					jx9MemObjStore(pObj, pTos);
43883 				}
43884 			}
43885 		}else{
43886 			if( pInstr->iP1 ){
43887 				/* Force a numeric cast */
43888 				jx9MemObjToNumeric(pTos);
43889 				/* Pre-increment */
43890 				if( pTos->iFlags & MEMOBJ_REAL ){
43891 					pTos->x.rVal++;
43892 					/* Try to get an integer representation */
43893 					jx9MemObjTryInteger(pTos);
43894 				}else{
43895 					pTos->x.iVal++;
43896 					MemObjSetType(pTos, MEMOBJ_INT);
43897 				}
43898 			}
43899 		}
43900 	}
43901 	break;
43902 /*
43903  * DECR: P1 * *
43904  *
43905  * Force a numeric cast and decrement the top of the stack by 1.
43906  * If the P1 operand is set then perform a duplication of the top of the stack
43907  * and decrement after that.
43908  */
43909 case JX9_OP_DECR:
43910 #ifdef UNTRUST
43911 	if( pTos < pStack ){
43912 		goto Abort;
43913 	}
43914 #endif
43915 	if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
43916 		/* Force a numeric cast */
43917 		jx9MemObjToNumeric(pTos);
43918 		if( pTos->nIdx != SXU32_HIGH ){
43919 			jx9_value *pObj;
43920 			if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
43921 				/* Force a numeric cast */
43922 				jx9MemObjToNumeric(pObj);
43923 				if( pObj->iFlags & MEMOBJ_REAL ){
43924 					pObj->x.rVal--;
43925 					/* Try to get an integer representation */
43926 					jx9MemObjTryInteger(pTos);
43927 				}else{
43928 					pObj->x.iVal--;
43929 					MemObjSetType(pTos, MEMOBJ_INT);
43930 				}
43931 				if( pInstr->iP1 ){
43932 					/* Pre-icrement */
43933 					jx9MemObjStore(pObj, pTos);
43934 				}
43935 			}
43936 		}else{
43937 			if( pInstr->iP1 ){
43938 				/* Pre-increment */
43939 				if( pTos->iFlags & MEMOBJ_REAL ){
43940 					pTos->x.rVal--;
43941 					/* Try to get an integer representation */
43942 					jx9MemObjTryInteger(pTos);
43943 				}else{
43944 					pTos->x.iVal--;
43945 					MemObjSetType(pTos, MEMOBJ_INT);
43946 				}
43947 			}
43948 		}
43949 	}
43950 	break;
43951 /*
43952  * UMINUS: * * *
43953  *
43954  * Perform a unary minus operation.
43955  */
43956 case JX9_OP_UMINUS:
43957 #ifdef UNTRUST
43958 	if( pTos < pStack ){
43959 		goto Abort;
43960 	}
43961 #endif
43962 	/* Force a numeric (integer, real or both) cast */
43963 	jx9MemObjToNumeric(pTos);
43964 	if( pTos->iFlags & MEMOBJ_REAL ){
43965 		pTos->x.rVal = -pTos->x.rVal;
43966 	}
43967 	if( pTos->iFlags & MEMOBJ_INT ){
43968 		pTos->x.iVal = -pTos->x.iVal;
43969 	}
43970 	break;
43971 /*
43972  * UPLUS: * * *
43973  *
43974  * Perform a unary plus operation.
43975  */
43976 case JX9_OP_UPLUS:
43977 #ifdef UNTRUST
43978 	if( pTos < pStack ){
43979 		goto Abort;
43980 	}
43981 #endif
43982 	/* Force a numeric (integer, real or both) cast */
43983 	jx9MemObjToNumeric(pTos);
43984 	if( pTos->iFlags & MEMOBJ_REAL ){
43985 		pTos->x.rVal = +pTos->x.rVal;
43986 	}
43987 	if( pTos->iFlags & MEMOBJ_INT ){
43988 		pTos->x.iVal = +pTos->x.iVal;
43989 	}
43990 	break;
43991 /*
43992  * OP_LNOT: * * *
43993  *
43994  * Interpret the top of the stack as a boolean value.  Replace it
43995  * with its complement.
43996  */
43997 case JX9_OP_LNOT:
43998 #ifdef UNTRUST
43999 	if( pTos < pStack ){
44000 		goto Abort;
44001 	}
44002 #endif
44003 	/* Force a boolean cast */
44004 	if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44005 		jx9MemObjToBool(pTos);
44006 	}
44007 	pTos->x.iVal = !pTos->x.iVal;
44008 	break;
44009 /*
44010  * OP_BITNOT: * * *
44011  *
44012  * Interpret the top of the stack as an value.Replace it
44013  * with its ones-complement.
44014  */
44015 case JX9_OP_BITNOT:
44016 #ifdef UNTRUST
44017 	if( pTos < pStack ){
44018 		goto Abort;
44019 	}
44020 #endif
44021 	/* Force an integer cast */
44022 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44023 		jx9MemObjToInteger(pTos);
44024 	}
44025 	pTos->x.iVal = ~pTos->x.iVal;
44026 	break;
44027 /* OP_MUL * * *
44028  * OP_MUL_STORE * * *
44029  *
44030  * Pop the top two elements from the stack, multiply them together,
44031  * and push the result back onto the stack.
44032  */
44033 case JX9_OP_MUL:
44034 case JX9_OP_MUL_STORE: {
44035 	jx9_value *pNos = &pTos[-1];
44036 	/* Force the operand to be numeric */
44037 #ifdef UNTRUST
44038 	if( pNos < pStack ){
44039 		goto Abort;
44040 	}
44041 #endif
44042 	jx9MemObjToNumeric(pTos);
44043 	jx9MemObjToNumeric(pNos);
44044 	/* Perform the requested operation */
44045 	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44046 		/* Floating point arithemic */
44047 		jx9_real a, b, r;
44048 		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44049 			jx9MemObjToReal(pTos);
44050 		}
44051 		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44052 			jx9MemObjToReal(pNos);
44053 		}
44054 		a = pNos->x.rVal;
44055 		b = pTos->x.rVal;
44056 		r = a * b;
44057 		/* Push the result */
44058 		pNos->x.rVal = r;
44059 		MemObjSetType(pNos, MEMOBJ_REAL);
44060 		/* Try to get an integer representation */
44061 		jx9MemObjTryInteger(pNos);
44062 	}else{
44063 		/* Integer arithmetic */
44064 		sxi64 a, b, r;
44065 		a = pNos->x.iVal;
44066 		b = pTos->x.iVal;
44067 		r = a * b;
44068 		/* Push the result */
44069 		pNos->x.iVal = r;
44070 		MemObjSetType(pNos, MEMOBJ_INT);
44071 	}
44072 	if( pInstr->iOp == JX9_OP_MUL_STORE ){
44073 		jx9_value *pObj;
44074 		if( pTos->nIdx == SXU32_HIGH ){
44075 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44076 		}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44077 			jx9MemObjStore(pNos, pObj);
44078 		}
44079 	}
44080 	VmPopOperand(&pTos, 1);
44081 	break;
44082 				 }
44083 /* OP_ADD * * *
44084  *
44085  * Pop the top two elements from the stack, add them together,
44086  * and push the result back onto the stack.
44087  */
44088 case JX9_OP_ADD:{
44089 	jx9_value *pNos = &pTos[-1];
44090 #ifdef UNTRUST
44091 	if( pNos < pStack ){
44092 		goto Abort;
44093 	}
44094 #endif
44095 	/* Perform the addition */
44096 	jx9MemObjAdd(pNos, pTos, FALSE);
44097 	VmPopOperand(&pTos, 1);
44098 	break;
44099 				}
44100 /*
44101  * OP_ADD_STORE * * *
44102  *
44103  * Pop the top two elements from the stack, add them together,
44104  * and push the result back onto the stack.
44105  */
44106 case JX9_OP_ADD_STORE:{
44107 	jx9_value *pNos = &pTos[-1];
44108 	jx9_value *pObj;
44109 	sxu32 nIdx;
44110 #ifdef UNTRUST
44111 	if( pNos < pStack ){
44112 		goto Abort;
44113 	}
44114 #endif
44115 	/* Perform the addition */
44116 	nIdx = pTos->nIdx;
44117 	jx9MemObjAdd(pTos, pNos, TRUE);
44118 	/* Peform the store operation */
44119 	if( nIdx == SXU32_HIGH ){
44120 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44121 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
44122 		jx9MemObjStore(pTos, pObj);
44123 	}
44124 	/* Ticket 1433-35: Perform a stack dup */
44125 	jx9MemObjStore(pTos, pNos);
44126 	VmPopOperand(&pTos, 1);
44127 	break;
44128 				}
44129 /* OP_SUB * * *
44130  *
44131  * Pop the top two elements from the stack, subtract the
44132  * first (what was next on the stack) from the second (the
44133  * top of the stack) and push the result back onto the stack.
44134  */
44135 case JX9_OP_SUB: {
44136 	jx9_value *pNos = &pTos[-1];
44137 #ifdef UNTRUST
44138 	if( pNos < pStack ){
44139 		goto Abort;
44140 	}
44141 #endif
44142 	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44143 		/* Floating point arithemic */
44144 		jx9_real a, b, r;
44145 		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44146 			jx9MemObjToReal(pTos);
44147 		}
44148 		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44149 			jx9MemObjToReal(pNos);
44150 		}
44151 		a = pNos->x.rVal;
44152 		b = pTos->x.rVal;
44153 		r = a - b;
44154 		/* Push the result */
44155 		pNos->x.rVal = r;
44156 		MemObjSetType(pNos, MEMOBJ_REAL);
44157 		/* Try to get an integer representation */
44158 		jx9MemObjTryInteger(pNos);
44159 	}else{
44160 		/* Integer arithmetic */
44161 		sxi64 a, b, r;
44162 		a = pNos->x.iVal;
44163 		b = pTos->x.iVal;
44164 		r = a - b;
44165 		/* Push the result */
44166 		pNos->x.iVal = r;
44167 		MemObjSetType(pNos, MEMOBJ_INT);
44168 	}
44169 	VmPopOperand(&pTos, 1);
44170 	break;
44171 				 }
44172 /* OP_SUB_STORE * * *
44173  *
44174  * Pop the top two elements from the stack, subtract the
44175  * first (what was next on the stack) from the second (the
44176  * top of the stack) and push the result back onto the stack.
44177  */
44178 case JX9_OP_SUB_STORE: {
44179 	jx9_value *pNos = &pTos[-1];
44180 	jx9_value *pObj;
44181 #ifdef UNTRUST
44182 	if( pNos < pStack ){
44183 		goto Abort;
44184 	}
44185 #endif
44186 	if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
44187 		/* Floating point arithemic */
44188 		jx9_real a, b, r;
44189 		if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44190 			jx9MemObjToReal(pTos);
44191 		}
44192 		if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44193 			jx9MemObjToReal(pNos);
44194 		}
44195 		a = pTos->x.rVal;
44196 		b = pNos->x.rVal;
44197 		r = a - b;
44198 		/* Push the result */
44199 		pNos->x.rVal = r;
44200 		MemObjSetType(pNos, MEMOBJ_REAL);
44201 		/* Try to get an integer representation */
44202 		jx9MemObjTryInteger(pNos);
44203 	}else{
44204 		/* Integer arithmetic */
44205 		sxi64 a, b, r;
44206 		a = pTos->x.iVal;
44207 		b = pNos->x.iVal;
44208 		r = a - b;
44209 		/* Push the result */
44210 		pNos->x.iVal = r;
44211 		MemObjSetType(pNos, MEMOBJ_INT);
44212 	}
44213 	if( pTos->nIdx == SXU32_HIGH ){
44214 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44215 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44216 		jx9MemObjStore(pNos, pObj);
44217 	}
44218 	VmPopOperand(&pTos, 1);
44219 	break;
44220 				 }
44221 
44222 /*
44223  * OP_MOD * * *
44224  *
44225  * Pop the top two elements from the stack, divide the
44226  * first (what was next on the stack) from the second (the
44227  * top of the stack) and push the remainder after division
44228  * onto the stack.
44229  * Note: Only integer arithemtic is allowed.
44230  */
44231 case JX9_OP_MOD:{
44232 	jx9_value *pNos = &pTos[-1];
44233 	sxi64 a, b, r;
44234 #ifdef UNTRUST
44235 	if( pNos < pStack ){
44236 		goto Abort;
44237 	}
44238 #endif
44239 	/* Force the operands to be integer */
44240 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44241 		jx9MemObjToInteger(pTos);
44242 	}
44243 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44244 		jx9MemObjToInteger(pNos);
44245 	}
44246 	/* Perform the requested operation */
44247 	a = pNos->x.iVal;
44248 	b = pTos->x.iVal;
44249 	if( b == 0 ){
44250 		r = 0;
44251 		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
44252 		/* goto Abort; */
44253 	}else{
44254 		r = a%b;
44255 	}
44256 	/* Push the result */
44257 	pNos->x.iVal = r;
44258 	MemObjSetType(pNos, MEMOBJ_INT);
44259 	VmPopOperand(&pTos, 1);
44260 	break;
44261 				}
44262 /*
44263  * OP_MOD_STORE * * *
44264  *
44265  * Pop the top two elements from the stack, divide the
44266  * first (what was next on the stack) from the second (the
44267  * top of the stack) and push the remainder after division
44268  * onto the stack.
44269  * Note: Only integer arithemtic is allowed.
44270  */
44271 case JX9_OP_MOD_STORE: {
44272 	jx9_value *pNos = &pTos[-1];
44273 	jx9_value *pObj;
44274 	sxi64 a, b, r;
44275 #ifdef UNTRUST
44276 	if( pNos < pStack ){
44277 		goto Abort;
44278 	}
44279 #endif
44280 	/* Force the operands to be integer */
44281 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44282 		jx9MemObjToInteger(pTos);
44283 	}
44284 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44285 		jx9MemObjToInteger(pNos);
44286 	}
44287 	/* Perform the requested operation */
44288 	a = pTos->x.iVal;
44289 	b = pNos->x.iVal;
44290 	if( b == 0 ){
44291 		r = 0;
44292 		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
44293 		/* goto Abort; */
44294 	}else{
44295 		r = a%b;
44296 	}
44297 	/* Push the result */
44298 	pNos->x.iVal = r;
44299 	MemObjSetType(pNos, MEMOBJ_INT);
44300 	if( pTos->nIdx == SXU32_HIGH ){
44301 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44302 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44303 		jx9MemObjStore(pNos, pObj);
44304 	}
44305 	VmPopOperand(&pTos, 1);
44306 	break;
44307 				}
44308 /*
44309  * OP_DIV * * *
44310  *
44311  * Pop the top two elements from the stack, divide the
44312  * first (what was next on the stack) from the second (the
44313  * top of the stack) and push the result onto the stack.
44314  * Note: Only floating point arithemtic is allowed.
44315  */
44316 case JX9_OP_DIV:{
44317 	jx9_value *pNos = &pTos[-1];
44318 	jx9_real a, b, r;
44319 #ifdef UNTRUST
44320 	if( pNos < pStack ){
44321 		goto Abort;
44322 	}
44323 #endif
44324 	/* Force the operands to be real */
44325 	if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44326 		jx9MemObjToReal(pTos);
44327 	}
44328 	if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44329 		jx9MemObjToReal(pNos);
44330 	}
44331 	/* Perform the requested operation */
44332 	a = pNos->x.rVal;
44333 	b = pTos->x.rVal;
44334 	if( b == 0 ){
44335 		/* Division by zero */
44336 		r = 0;
44337 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
44338 		/* goto Abort; */
44339 	}else{
44340 		r = a/b;
44341 		/* Push the result */
44342 		pNos->x.rVal = r;
44343 		MemObjSetType(pNos, MEMOBJ_REAL);
44344 		/* Try to get an integer representation */
44345 		jx9MemObjTryInteger(pNos);
44346 	}
44347 	VmPopOperand(&pTos, 1);
44348 	break;
44349 				}
44350 /*
44351  * OP_DIV_STORE * * *
44352  *
44353  * Pop the top two elements from the stack, divide the
44354  * first (what was next on the stack) from the second (the
44355  * top of the stack) and push the result onto the stack.
44356  * Note: Only floating point arithemtic is allowed.
44357  */
44358 case JX9_OP_DIV_STORE:{
44359 	jx9_value *pNos = &pTos[-1];
44360 	jx9_value *pObj;
44361 	jx9_real a, b, r;
44362 #ifdef UNTRUST
44363 	if( pNos < pStack ){
44364 		goto Abort;
44365 	}
44366 #endif
44367 	/* Force the operands to be real */
44368 	if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
44369 		jx9MemObjToReal(pTos);
44370 	}
44371 	if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
44372 		jx9MemObjToReal(pNos);
44373 	}
44374 	/* Perform the requested operation */
44375 	a = pTos->x.rVal;
44376 	b = pNos->x.rVal;
44377 	if( b == 0 ){
44378 		/* Division by zero */
44379 		r = 0;
44380 		VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
44381 		/* goto Abort; */
44382 	}else{
44383 		r = a/b;
44384 		/* Push the result */
44385 		pNos->x.rVal = r;
44386 		MemObjSetType(pNos, MEMOBJ_REAL);
44387 		/* Try to get an integer representation */
44388 		jx9MemObjTryInteger(pNos);
44389 	}
44390 	if( pTos->nIdx == SXU32_HIGH ){
44391 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44392 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44393 		jx9MemObjStore(pNos, pObj);
44394 	}
44395 	VmPopOperand(&pTos, 1);
44396 	break;
44397 				}
44398 /* OP_BAND * * *
44399  *
44400  * Pop the top two elements from the stack.  Convert both elements
44401  * to integers.  Push back onto the stack the bit-wise AND of the
44402  * two elements.
44403 */
44404 /* OP_BOR * * *
44405  *
44406  * Pop the top two elements from the stack.  Convert both elements
44407  * to integers.  Push back onto the stack the bit-wise OR of the
44408  * two elements.
44409  */
44410 /* OP_BXOR * * *
44411  *
44412  * Pop the top two elements from the stack.  Convert both elements
44413  * to integers.  Push back onto the stack the bit-wise XOR of the
44414  * two elements.
44415  */
44416 case JX9_OP_BAND:
44417 case JX9_OP_BOR:
44418 case JX9_OP_BXOR:{
44419 	jx9_value *pNos = &pTos[-1];
44420 	sxi64 a, b, r;
44421 #ifdef UNTRUST
44422 	if( pNos < pStack ){
44423 		goto Abort;
44424 	}
44425 #endif
44426 	/* Force the operands to be integer */
44427 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44428 		jx9MemObjToInteger(pTos);
44429 	}
44430 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44431 		jx9MemObjToInteger(pNos);
44432 	}
44433 	/* Perform the requested operation */
44434 	a = pNos->x.iVal;
44435 	b = pTos->x.iVal;
44436 	switch(pInstr->iOp){
44437 	case JX9_OP_BOR_STORE:
44438 	case JX9_OP_BOR:  r = a|b; break;
44439 	case JX9_OP_BXOR_STORE:
44440 	case JX9_OP_BXOR: r = a^b; break;
44441 	case JX9_OP_BAND_STORE:
44442 	case JX9_OP_BAND:
44443 	default:          r = a&b; break;
44444 	}
44445 	/* Push the result */
44446 	pNos->x.iVal = r;
44447 	MemObjSetType(pNos, MEMOBJ_INT);
44448 	VmPopOperand(&pTos, 1);
44449 	break;
44450 				 }
44451 /* OP_BAND_STORE * * *
44452  *
44453  * Pop the top two elements from the stack.  Convert both elements
44454  * to integers.  Push back onto the stack the bit-wise AND of the
44455  * two elements.
44456 */
44457 /* OP_BOR_STORE * * *
44458  *
44459  * Pop the top two elements from the stack.  Convert both elements
44460  * to integers.  Push back onto the stack the bit-wise OR of the
44461  * two elements.
44462  */
44463 /* OP_BXOR_STORE * * *
44464  *
44465  * Pop the top two elements from the stack.  Convert both elements
44466  * to integers.  Push back onto the stack the bit-wise XOR of the
44467  * two elements.
44468  */
44469 case JX9_OP_BAND_STORE:
44470 case JX9_OP_BOR_STORE:
44471 case JX9_OP_BXOR_STORE:{
44472 	jx9_value *pNos = &pTos[-1];
44473 	jx9_value *pObj;
44474 	sxi64 a, b, r;
44475 #ifdef UNTRUST
44476 	if( pNos < pStack ){
44477 		goto Abort;
44478 	}
44479 #endif
44480 	/* Force the operands to be integer */
44481 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44482 		jx9MemObjToInteger(pTos);
44483 	}
44484 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44485 		jx9MemObjToInteger(pNos);
44486 	}
44487 	/* Perform the requested operation */
44488 	a = pTos->x.iVal;
44489 	b = pNos->x.iVal;
44490 	switch(pInstr->iOp){
44491 	case JX9_OP_BOR_STORE:
44492 	case JX9_OP_BOR:  r = a|b; break;
44493 	case JX9_OP_BXOR_STORE:
44494 	case JX9_OP_BXOR: r = a^b; break;
44495 	case JX9_OP_BAND_STORE:
44496 	case JX9_OP_BAND:
44497 	default:          r = a&b; break;
44498 	}
44499 	/* Push the result */
44500 	pNos->x.iVal = r;
44501 	MemObjSetType(pNos, MEMOBJ_INT);
44502 	if( pTos->nIdx == SXU32_HIGH ){
44503 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44504 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44505 		jx9MemObjStore(pNos, pObj);
44506 	}
44507 	VmPopOperand(&pTos, 1);
44508 	break;
44509 				 }
44510 /* OP_SHL * * *
44511  *
44512  * Pop the top two elements from the stack.  Convert both elements
44513  * to integers.  Push back onto the stack the second element shifted
44514  * left by N bits where N is the top element on the stack.
44515  * Note: Only integer arithmetic is allowed.
44516  */
44517 /* OP_SHR * * *
44518  *
44519  * Pop the top two elements from the stack.  Convert both elements
44520  * to integers.  Push back onto the stack the second element shifted
44521  * right by N bits where N is the top element on the stack.
44522  * Note: Only integer arithmetic is allowed.
44523  */
44524 case JX9_OP_SHL:
44525 case JX9_OP_SHR: {
44526 	jx9_value *pNos = &pTos[-1];
44527 	sxi64 a, r;
44528 	sxi32 b;
44529 #ifdef UNTRUST
44530 	if( pNos < pStack ){
44531 		goto Abort;
44532 	}
44533 #endif
44534 	/* Force the operands to be integer */
44535 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44536 		jx9MemObjToInteger(pTos);
44537 	}
44538 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44539 		jx9MemObjToInteger(pNos);
44540 	}
44541 	/* Perform the requested operation */
44542 	a = pNos->x.iVal;
44543 	b = (sxi32)pTos->x.iVal;
44544 	if( pInstr->iOp == JX9_OP_SHL ){
44545 		r = a << b;
44546 	}else{
44547 		r = a >> b;
44548 	}
44549 	/* Push the result */
44550 	pNos->x.iVal = r;
44551 	MemObjSetType(pNos, MEMOBJ_INT);
44552 	VmPopOperand(&pTos, 1);
44553 	break;
44554 				 }
44555 /*  OP_SHL_STORE * * *
44556  *
44557  * Pop the top two elements from the stack.  Convert both elements
44558  * to integers.  Push back onto the stack the second element shifted
44559  * left by N bits where N is the top element on the stack.
44560  * Note: Only integer arithmetic is allowed.
44561  */
44562 /* OP_SHR_STORE * * *
44563  *
44564  * Pop the top two elements from the stack.  Convert both elements
44565  * to integers.  Push back onto the stack the second element shifted
44566  * right by N bits where N is the top element on the stack.
44567  * Note: Only integer arithmetic is allowed.
44568  */
44569 case JX9_OP_SHL_STORE:
44570 case JX9_OP_SHR_STORE: {
44571 	jx9_value *pNos = &pTos[-1];
44572 	jx9_value *pObj;
44573 	sxi64 a, r;
44574 	sxi32 b;
44575 #ifdef UNTRUST
44576 	if( pNos < pStack ){
44577 		goto Abort;
44578 	}
44579 #endif
44580 	/* Force the operands to be integer */
44581 	if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
44582 		jx9MemObjToInteger(pTos);
44583 	}
44584 	if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
44585 		jx9MemObjToInteger(pNos);
44586 	}
44587 	/* Perform the requested operation */
44588 	a = pTos->x.iVal;
44589 	b = (sxi32)pNos->x.iVal;
44590 	if( pInstr->iOp == JX9_OP_SHL_STORE ){
44591 		r = a << b;
44592 	}else{
44593 		r = a >> b;
44594 	}
44595 	/* Push the result */
44596 	pNos->x.iVal = r;
44597 	MemObjSetType(pNos, MEMOBJ_INT);
44598 	if( pTos->nIdx == SXU32_HIGH ){
44599 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44600 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44601 		jx9MemObjStore(pNos, pObj);
44602 	}
44603 	VmPopOperand(&pTos, 1);
44604 	break;
44605 				 }
44606 /* CAT:  P1 * *
44607  *
44608  * Pop P1 elements from the stack. Concatenate them togeher and push the result
44609  * back.
44610  */
44611 case JX9_OP_CAT:{
44612 	jx9_value *pNos, *pCur;
44613 	if( pInstr->iP1 < 1 ){
44614 		pNos = &pTos[-1];
44615 	}else{
44616 		pNos = &pTos[-pInstr->iP1+1];
44617 	}
44618 #ifdef UNTRUST
44619 	if( pNos < pStack ){
44620 		goto Abort;
44621 	}
44622 #endif
44623 	/* Force a string cast */
44624 	if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){
44625 		jx9MemObjToString(pNos);
44626 	}
44627 	pCur = &pNos[1];
44628 	while( pCur <= pTos ){
44629 		if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
44630 			jx9MemObjToString(pCur);
44631 		}
44632 		/* Perform the concatenation */
44633 		if( SyBlobLength(&pCur->sBlob) > 0 ){
44634 			jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob));
44635 		}
44636 		SyBlobRelease(&pCur->sBlob);
44637 		pCur++;
44638 	}
44639 	pTos = pNos;
44640 	break;
44641 				}
44642 /*  CAT_STORE: * * *
44643  *
44644  * Pop two elements from the stack. Concatenate them togeher and push the result
44645  * back.
44646  */
44647 case JX9_OP_CAT_STORE:{
44648 	jx9_value *pNos = &pTos[-1];
44649 	jx9_value *pObj;
44650 #ifdef UNTRUST
44651 	if( pNos < pStack ){
44652 		goto Abort;
44653 	}
44654 #endif
44655 	if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
44656 		/* Force a string cast */
44657 		jx9MemObjToString(pTos);
44658 	}
44659 	if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
44660 		/* Force a string cast */
44661 		jx9MemObjToString(pNos);
44662 	}
44663 	/* Perform the concatenation (Reverse order) */
44664 	if( SyBlobLength(&pNos->sBlob) > 0 ){
44665 		jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
44666 	}
44667 	/* Perform the store operation */
44668 	if( pTos->nIdx == SXU32_HIGH ){
44669 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
44670 	}else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
44671 		jx9MemObjStore(pTos, pObj);
44672 	}
44673 	jx9MemObjStore(pTos, pNos);
44674 	VmPopOperand(&pTos, 1);
44675 	break;
44676 				}
44677 /* OP_AND: * * *
44678  *
44679  * Pop two values off the stack.  Take the logical AND of the
44680  * two values and push the resulting boolean value back onto the
44681  * stack.
44682  */
44683 /* OP_OR: * * *
44684  *
44685  * Pop two values off the stack.  Take the logical OR of the
44686  * two values and push the resulting boolean value back onto the
44687  * stack.
44688  */
44689 case JX9_OP_LAND:
44690 case JX9_OP_LOR: {
44691 	jx9_value *pNos = &pTos[-1];
44692 	sxi32 v1, v2;    /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
44693 #ifdef UNTRUST
44694 	if( pNos < pStack ){
44695 		goto Abort;
44696 	}
44697 #endif
44698 	/* Force a boolean cast */
44699 	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44700 		jx9MemObjToBool(pTos);
44701 	}
44702 	if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
44703 		jx9MemObjToBool(pNos);
44704 	}
44705 	v1 = pNos->x.iVal == 0 ? 1 : 0;
44706 	v2 = pTos->x.iVal == 0 ? 1 : 0;
44707 	if( pInstr->iOp == JX9_OP_LAND ){
44708 		static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
44709 		v1 = and_logic[v1*3+v2];
44710 	}else{
44711 		static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
44712 		v1 = or_logic[v1*3+v2];
44713 	}
44714 	if( v1 == 2 ){
44715 		v1 = 1;
44716 	}
44717 	VmPopOperand(&pTos, 1);
44718 	pTos->x.iVal = v1 == 0 ? 1 : 0;
44719 	MemObjSetType(pTos, MEMOBJ_BOOL);
44720 	break;
44721 				 }
44722 /* OP_LXOR: * * *
44723  *
44724  * Pop two values off the stack. Take the logical XOR of the
44725  * two values and push the resulting boolean value back onto the
44726  * stack.
44727  * According to the JX9 language reference manual:
44728  *  $a xor $b is evaluated to TRUE if either $a or $b is
44729  *  TRUE, but not both.
44730  */
44731 case JX9_OP_LXOR:{
44732 	jx9_value *pNos = &pTos[-1];
44733 	sxi32 v = 0;
44734 #ifdef UNTRUST
44735 	if( pNos < pStack ){
44736 		goto Abort;
44737 	}
44738 #endif
44739 	/* Force a boolean cast */
44740 	if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
44741 		jx9MemObjToBool(pTos);
44742 	}
44743 	if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
44744 		jx9MemObjToBool(pNos);
44745 	}
44746 	if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){
44747 		v = 1;
44748 	}
44749 	VmPopOperand(&pTos, 1);
44750 	pTos->x.iVal = v;
44751 	MemObjSetType(pTos, MEMOBJ_BOOL);
44752 	break;
44753 				 }
44754 /* OP_EQ P1 P2 P3
44755  *
44756  * Pop the top two elements from the stack.  If they are equal, then
44757  * jump to instruction P2.  Otherwise, continue to the next instruction.
44758  * If P2 is zero, do not jump.  Instead, push a boolean 1 (TRUE) onto the
44759  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44760  */
44761 /* OP_NEQ P1 P2 P3
44762  *
44763  * Pop the top two elements from the stack. If they are not equal, then
44764  * jump to instruction P2. Otherwise, continue to the next instruction.
44765  * If P2 is zero, do not jump.  Instead, push a boolean 1 (TRUE) onto the
44766  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44767  */
44768 case JX9_OP_EQ:
44769 case JX9_OP_NEQ: {
44770 	jx9_value *pNos = &pTos[-1];
44771 	/* Perform the comparison and act accordingly */
44772 #ifdef UNTRUST
44773 	if( pNos < pStack ){
44774 		goto Abort;
44775 	}
44776 #endif
44777 	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
44778 	if( pInstr->iOp == JX9_OP_EQ ){
44779 		rc = rc == 0;
44780 	}else{
44781 		rc = rc != 0;
44782 	}
44783 	VmPopOperand(&pTos, 1);
44784 	if( !pInstr->iP2 ){
44785 		/* Push comparison result without taking the jump */
44786 		jx9MemObjRelease(pTos);
44787 		pTos->x.iVal = rc;
44788 		/* Invalidate any prior representation */
44789 		MemObjSetType(pTos, MEMOBJ_BOOL);
44790 	}else{
44791 		if( rc ){
44792 			/* Jump to the desired location */
44793 			pc = pInstr->iP2 - 1;
44794 			VmPopOperand(&pTos, 1);
44795 		}
44796 	}
44797 	break;
44798 				 }
44799 /* OP_TEQ P1 P2 *
44800  *
44801  * Pop the top two elements from the stack. If they have the same type and are equal
44802  * then jump to instruction P2. Otherwise, continue to the next instruction.
44803  * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44804  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44805  */
44806 case JX9_OP_TEQ: {
44807 	jx9_value *pNos = &pTos[-1];
44808 	/* Perform the comparison and act accordingly */
44809 #ifdef UNTRUST
44810 	if( pNos < pStack ){
44811 		goto Abort;
44812 	}
44813 #endif
44814 	rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
44815 	VmPopOperand(&pTos, 1);
44816 	if( !pInstr->iP2 ){
44817 		/* Push comparison result without taking the jump */
44818 		jx9MemObjRelease(pTos);
44819 		pTos->x.iVal = rc;
44820 		/* Invalidate any prior representation */
44821 		MemObjSetType(pTos, MEMOBJ_BOOL);
44822 	}else{
44823 		if( rc ){
44824 			/* Jump to the desired location */
44825 			pc = pInstr->iP2 - 1;
44826 			VmPopOperand(&pTos, 1);
44827 		}
44828 	}
44829 	break;
44830 				 }
44831 /* OP_TNE P1 P2 *
44832  *
44833  * Pop the top two elements from the stack.If they are not equal an they are not
44834  * of the same type, then jump to instruction P2. Otherwise, continue to the next
44835  * instruction.
44836  * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
44837  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44838  *
44839  */
44840 case JX9_OP_TNE: {
44841 	jx9_value *pNos = &pTos[-1];
44842 	/* Perform the comparison and act accordingly */
44843 #ifdef UNTRUST
44844 	if( pNos < pStack ){
44845 		goto Abort;
44846 	}
44847 #endif
44848 	rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
44849 	VmPopOperand(&pTos, 1);
44850 	if( !pInstr->iP2 ){
44851 		/* Push comparison result without taking the jump */
44852 		jx9MemObjRelease(pTos);
44853 		pTos->x.iVal = rc;
44854 		/* Invalidate any prior representation */
44855 		MemObjSetType(pTos, MEMOBJ_BOOL);
44856 	}else{
44857 		if( rc ){
44858 			/* Jump to the desired location */
44859 			pc = pInstr->iP2 - 1;
44860 			VmPopOperand(&pTos, 1);
44861 		}
44862 	}
44863 	break;
44864 				 }
44865 /* OP_LT P1 P2 P3
44866  *
44867  * Pop the top two elements from the stack. If the second element (the top of stack)
44868  * is less than the first (next on stack), then jump to instruction P2.Otherwise
44869  * continue to the next instruction. In other words, jump if pNos<pTos.
44870  * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44871  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44872  *
44873  */
44874 /* OP_LE P1 P2 P3
44875  *
44876  * Pop the top two elements from the stack. If the second element (the top of stack)
44877  * is less than or equal to the first (next on stack), then jump to instruction P2.
44878  * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
44879  * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44880  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44881  *
44882  */
44883 case JX9_OP_LT:
44884 case JX9_OP_LE: {
44885 	jx9_value *pNos = &pTos[-1];
44886 	/* Perform the comparison and act accordingly */
44887 #ifdef UNTRUST
44888 	if( pNos < pStack ){
44889 		goto Abort;
44890 	}
44891 #endif
44892 	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
44893 	if( pInstr->iOp == JX9_OP_LE ){
44894 		rc = rc < 1;
44895 	}else{
44896 		rc = rc < 0;
44897 	}
44898 	VmPopOperand(&pTos, 1);
44899 	if( !pInstr->iP2 ){
44900 		/* Push comparison result without taking the jump */
44901 		jx9MemObjRelease(pTos);
44902 		pTos->x.iVal = rc;
44903 		/* Invalidate any prior representation */
44904 		MemObjSetType(pTos, MEMOBJ_BOOL);
44905 	}else{
44906 		if( rc ){
44907 			/* Jump to the desired location */
44908 			pc = pInstr->iP2 - 1;
44909 			VmPopOperand(&pTos, 1);
44910 		}
44911 	}
44912 	break;
44913 				}
44914 /* OP_GT P1 P2 P3
44915  *
44916  * Pop the top two elements from the stack. If the second element (the top of stack)
44917  * is greater than the first (next on stack), then jump to instruction P2.Otherwise
44918  * continue to the next instruction. In other words, jump if pNos<pTos.
44919  * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44920  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44921  *
44922  */
44923 /* OP_GE P1 P2 P3
44924  *
44925  * Pop the top two elements from the stack. If the second element (the top of stack)
44926  * is greater than or equal to the first (next on stack), then jump to instruction P2.
44927  * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
44928  * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
44929  * stack if the jump would have been taken, or a 0 (FALSE) if not.
44930  *
44931  */
44932 case JX9_OP_GT:
44933 case JX9_OP_GE: {
44934 	jx9_value *pNos = &pTos[-1];
44935 	/* Perform the comparison and act accordingly */
44936 #ifdef UNTRUST
44937 	if( pNos < pStack ){
44938 		goto Abort;
44939 	}
44940 #endif
44941 	rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
44942 	if( pInstr->iOp == JX9_OP_GE ){
44943 		rc = rc >= 0;
44944 	}else{
44945 		rc = rc > 0;
44946 	}
44947 	VmPopOperand(&pTos, 1);
44948 	if( !pInstr->iP2 ){
44949 		/* Push comparison result without taking the jump */
44950 		jx9MemObjRelease(pTos);
44951 		pTos->x.iVal = rc;
44952 		/* Invalidate any prior representation */
44953 		MemObjSetType(pTos, MEMOBJ_BOOL);
44954 	}else{
44955 		if( rc ){
44956 			/* Jump to the desired location */
44957 			pc = pInstr->iP2 - 1;
44958 			VmPopOperand(&pTos, 1);
44959 		}
44960 	}
44961 	break;
44962 				}
44963 /*
44964  * OP_FOREACH_INIT * P2 P3
44965  * Prepare a foreach step.
44966  */
44967 case JX9_OP_FOREACH_INIT: {
44968 	jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
44969 	void *pName;
44970 #ifdef UNTRUST
44971 	if( pTos < pStack ){
44972 		goto Abort;
44973 	}
44974 #endif
44975 	if( SyStringLength(&pInfo->sValue) < 1 ){
44976 		/* Take the variable name from the top of the stack */
44977 		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
44978 			/* Force a string cast */
44979 			jx9MemObjToString(pTos);
44980 		}
44981 		/* Duplicate name */
44982 		if( SyBlobLength(&pTos->sBlob) > 0 ){
44983 			pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
44984 			SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob));
44985 		}
44986 		VmPopOperand(&pTos, 1);
44987 	}
44988 	if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
44989 		if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
44990 			/* Force a string cast */
44991 			jx9MemObjToString(pTos);
44992 		}
44993 		/* Duplicate name */
44994 		if( SyBlobLength(&pTos->sBlob) > 0 ){
44995 			pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
44996 			SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob));
44997 		}
44998 		VmPopOperand(&pTos, 1);
44999 	}
45000 	/* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/
45001 	if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
45002 		/* Jump out of the loop */
45003 		if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
45004 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
45005 				"Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
45006 		}
45007 		pc = pInstr->iP2 - 1;
45008 	}else{
45009 		jx9_foreach_step *pStep;
45010 		pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
45011 		if( pStep == 0 ){
45012 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
45013 			/* Jump out of the loop */
45014 			pc = pInstr->iP2 - 1;
45015 		}else{
45016 			/* Zero the structure */
45017 			SyZero(pStep, sizeof(jx9_foreach_step));
45018 			/* Prepare the step */
45019 			pStep->iFlags = pInfo->iFlags;
45020 			if( pTos->iFlags & MEMOBJ_HASHMAP ){
45021 				jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
45022 				/* Reset the internal loop cursor */
45023 				jx9HashmapResetLoopCursor(pMap);
45024 				/* Mark the step */
45025 				pStep->pMap = pMap;
45026 				pMap->iRef++;
45027 			}
45028 		}
45029 		if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
45030 			jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
45031 			SyMemBackendPoolFree(&pVm->sAllocator, pStep);
45032 			/* Jump out of the loop */
45033 			pc = pInstr->iP2 - 1;
45034 		}
45035 	}
45036 	VmPopOperand(&pTos, 1);
45037 	break;
45038 						  }
45039 /*
45040  * OP_FOREACH_STEP * P2 P3
45041  * Perform a foreach step. Jump to P2 at the end of the step.
45042  */
45043 case JX9_OP_FOREACH_STEP: {
45044 	jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
45045 	jx9_foreach_step **apStep, *pStep;
45046 	jx9_hashmap_node *pNode;
45047 	jx9_hashmap *pMap;
45048 	jx9_value *pValue;
45049 	/* Peek the last step */
45050 	apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
45051 	pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
45052 	pMap = pStep->pMap;
45053 	/* Extract the current node value */
45054 	pNode = jx9HashmapGetNextEntry(pMap);
45055 	if( pNode == 0 ){
45056 		/* No more entry to process */
45057 		pc = pInstr->iP2 - 1; /* Jump to this destination */
45058 		/* Automatically reset the loop cursor */
45059 		jx9HashmapResetLoopCursor(pMap);
45060 		/* Cleanup the mess left behind */
45061 		SyMemBackendPoolFree(&pVm->sAllocator, pStep);
45062 		SySetPop(&pInfo->aStep);
45063 		jx9HashmapUnref(pMap);
45064 	}else{
45065 		if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
45066 			jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
45067 			if( pKey ){
45068 				jx9HashmapExtractNodeKey(pNode, pKey);
45069 			}
45070 		}
45071 		/* Make a copy of the entry value */
45072 		pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
45073 		if( pValue ){
45074 			jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
45075 		}
45076 	}
45077 	break;
45078 						  }
45079 /*
45080  * OP_MEMBER P1 P2
45081  * Load JSON object entry on the stack.
45082  */
45083 case JX9_OP_MEMBER: {
45084 	jx9_hashmap_node *pNode = 0; /* cc warning */
45085 	jx9_hashmap *pMap = 0;
45086 	jx9_value *pIdx;
45087 	pIdx = pTos;
45088 	pTos--;
45089 	rc = SXERR_NOTFOUND; /* Assume the index is invalid */
45090 	if( pTos->iFlags & MEMOBJ_HASHMAP ){
45091 		/* Point to the hashmap */
45092 		pMap = (jx9_hashmap *)pTos->x.pOther;
45093 		/* Load the desired entry */
45094 		rc = jx9HashmapLookup(pMap, pIdx, &pNode);
45095 	}
45096 	jx9MemObjRelease(pIdx);
45097 	if( rc == SXRET_OK ){
45098 		/* Load entry contents */
45099 		if( pMap->iRef < 2 ){
45100 			/* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
45101 			 * of the entry value, rather than pointing to it.
45102 			 */
45103 			pTos->nIdx = SXU32_HIGH;
45104 			jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
45105 		}else{
45106 			pTos->nIdx = pNode->nValIdx;
45107 			jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
45108 			jx9HashmapUnref(pMap);
45109 		}
45110 	}else{
45111 		/* No such entry, load NULL */
45112 		jx9MemObjRelease(pTos);
45113 		pTos->nIdx = SXU32_HIGH;
45114 	}
45115 	break;
45116 					}
45117 /*
45118  * OP_SWITCH * * P3
45119  *  This is the bytecode implementation of the complex switch() JX9 construct.
45120  */
45121 case JX9_OP_SWITCH: {
45122 	jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
45123 	jx9_case_expr *aCase, *pCase;
45124 	jx9_value sValue, sCaseValue;
45125 	sxu32 n, nEntry;
45126 #ifdef UNTRUST
45127 	if( pSwitch == 0 || pTos < pStack ){
45128 		goto Abort;
45129 	}
45130 #endif
45131 	/* Point to the case table  */
45132 	aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
45133 	nEntry = SySetUsed(&pSwitch->aCaseExpr);
45134 	/* Select the appropriate case block to execute */
45135 	jx9MemObjInit(pVm, &sValue);
45136 	jx9MemObjInit(pVm, &sCaseValue);
45137 	for( n = 0 ; n < nEntry ; ++n ){
45138 		pCase = &aCase[n];
45139 		jx9MemObjLoad(pTos, &sValue);
45140 		/* Execute the case expression first */
45141 		VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
45142 		/* Compare the two expression */
45143 		rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
45144 		jx9MemObjRelease(&sValue);
45145 		jx9MemObjRelease(&sCaseValue);
45146 		if( rc == 0 ){
45147 			/* Value match, jump to this block */
45148 			pc = pCase->nStart - 1;
45149 			break;
45150 		}
45151 	}
45152 	VmPopOperand(&pTos, 1);
45153 	if( n >= nEntry ){
45154 		/* No approprite case to execute, jump to the default case */
45155 		if( pSwitch->nDefault > 0 ){
45156 			pc = pSwitch->nDefault - 1;
45157 		}else{
45158 			/* No default case, jump out of this switch */
45159 			pc = pSwitch->nOut - 1;
45160 		}
45161 	}
45162 	break;
45163 					}
45164 /*
45165  * OP_UPLINK P1 * *
45166  * Link a variable to the top active VM frame.
45167  * This is used to implement the 'uplink' JX9 construct.
45168  */
45169 case JX9_OP_UPLINK: {
45170 	if( pVm->pFrame->pParent ){
45171 		jx9_value *pLink = &pTos[-pInstr->iP1+1];
45172 		SyString sName;
45173 		/* Perform the link */
45174 		while( pLink <= pTos ){
45175 			if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
45176 				/* Force a string cast */
45177 				jx9MemObjToString(pLink);
45178 			}
45179 			SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
45180 			if( sName.nByte > 0 ){
45181 				VmFrameLink(&(*pVm), &sName);
45182 			}
45183 			pLink++;
45184 		}
45185 	}
45186 	VmPopOperand(&pTos, pInstr->iP1);
45187 	break;
45188 					}
45189 /*
45190  * OP_CALL P1 * *
45191  *  Call a JX9 or a foreign function and push the return value of the called
45192  *  function on the stack.
45193  */
45194 case JX9_OP_CALL: {
45195 	jx9_value *pArg = &pTos[-pInstr->iP1];
45196 	SyHashEntry *pEntry;
45197 	SyString sName;
45198 	/* Extract function name */
45199 	if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
45200 		/* Raise exception: Invalid function name */
45201 		VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
45202 		/* Pop given arguments */
45203 		if( pInstr->iP1 > 0 ){
45204 			VmPopOperand(&pTos, pInstr->iP1);
45205 		}
45206 		/* Assume a null return value so that the program continue it's execution normally */
45207 		jx9MemObjRelease(pTos);
45208 		break;
45209 	}
45210 	SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
45211 	/* Check for a compiled function first */
45212 	pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte);
45213 	if( pEntry ){
45214 		jx9_vm_func_arg *aFormalArg;
45215 		jx9_value *pFrameStack;
45216 		jx9_vm_func *pVmFunc;
45217 		VmFrame *pFrame;
45218 		jx9_value *pObj;
45219 		VmSlot sArg;
45220 		sxu32 n;
45221 		pVmFunc = (jx9_vm_func *)pEntry->pUserData;
45222 		/* Check The recursion limit */
45223 		if( pVm->nRecursionDepth > pVm->nMaxDepth ){
45224 			VmErrorFormat(&(*pVm), JX9_CTX_ERR,
45225 				"Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value",
45226 				&pVmFunc->sName);
45227 			/* Pop given arguments */
45228 			if( pInstr->iP1 > 0 ){
45229 				VmPopOperand(&pTos, pInstr->iP1);
45230 			}
45231 			/* Assume a null return value so that the program continue it's execution normally */
45232 			jx9MemObjRelease(pTos);
45233 			break;
45234 		}
45235 		if( pVmFunc->pNextName ){
45236 			/* Function is candidate for overloading, select the appropriate function to call */
45237 			pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg));
45238 		}
45239 		/* Extract the formal argument set */
45240 		aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
45241 		/* Create a new VM frame  */
45242 		rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
45243 		if( rc != SXRET_OK ){
45244 			/* Raise exception: Out of memory */
45245 			VmErrorFormat(&(*pVm), JX9_CTX_ERR,
45246 				"JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
45247 				&pVmFunc->sName);
45248 			/* Pop given arguments */
45249 			if( pInstr->iP1 > 0 ){
45250 				VmPopOperand(&pTos, pInstr->iP1);
45251 			}
45252 			/* Assume a null return value so that the program continue it's execution normally */
45253 			jx9MemObjRelease(pTos);
45254 			break;
45255 		}
45256 		if( SySetUsed(&pVmFunc->aStatic) > 0 ){
45257 			jx9_vm_func_static_var *pStatic, *aStatic;
45258 			/* Install static variables */
45259 			aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic);
45260 			for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){
45261 				pStatic = &aStatic[n];
45262 				if( pStatic->nIdx == SXU32_HIGH ){
45263 					/* Initialize the static variables */
45264 					pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx);
45265 					if( pObj ){
45266 						/* Assume a NULL initialization value */
45267 						jx9MemObjInit(&(*pVm), pObj);
45268 						if( SySetUsed(&pStatic->aByteCode) > 0 ){
45269 							/* Evaluate initialization expression (Any complex expression) */
45270 							VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj);
45271 						}
45272 						pObj->nIdx = pStatic->nIdx;
45273 					}else{
45274 						continue;
45275 					}
45276 				}
45277 				/* Install in the current frame */
45278 				SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName),
45279 					SX_INT_TO_PTR(pStatic->nIdx));
45280 			}
45281 		}
45282 		/* Push arguments in the local frame */
45283 		n = 0;
45284 		while( pArg < pTos ){
45285 			if( n < SySetUsed(&pVmFunc->aArgs) ){
45286 				if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
45287 					/* NULL values are redirected to default arguments */
45288 					rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg);
45289 					if( rc == JX9_ABORT ){
45290 						goto Abort;
45291 					}
45292 				}
45293 				/* Make sure the given arguments are of the correct type */
45294 				if( aFormalArg[n].nType > 0 ){
45295 				 if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
45296 						ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
45297 						/* Cast to the desired type */
45298 						if( xCast ){
45299 							xCast(pArg);
45300 						}
45301 					}
45302 				}
45303 				/* Pass by value, make a copy of the given argument */
45304 				pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
45305 			}else{
45306 				char zName[32];
45307 				SyString sName;
45308 				/* Set a dummy name */
45309 				sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n);
45310 				sName.zString = zName;
45311 				/* Annonymous argument */
45312 				pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE);
45313 			}
45314 			if( pObj ){
45315 				jx9MemObjStore(pArg, pObj);
45316 				/* Insert argument index  */
45317 				sArg.nIdx = pObj->nIdx;
45318 				sArg.pUserData = 0;
45319 				SySetPut(&pFrame->sArg, (const void *)&sArg);
45320 			}
45321 			jx9MemObjRelease(pArg);
45322 			pArg++;
45323 			++n;
45324 		}
45325 		/* Process default values */
45326 		while( n < SySetUsed(&pVmFunc->aArgs) ){
45327 			if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
45328 				pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
45329 				if( pObj ){
45330 					/* Evaluate the default value and extract it's result */
45331 					rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
45332 					if( rc == JX9_ABORT ){
45333 						goto Abort;
45334 					}
45335 					/* Insert argument index */
45336 					sArg.nIdx = pObj->nIdx;
45337 					sArg.pUserData = 0;
45338 					SySetPut(&pFrame->sArg, (const void *)&sArg);
45339 					/* Make sure the default argument is of the correct type */
45340 					if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){
45341 						ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
45342 						/* Cast to the desired type */
45343 						xCast(pObj);
45344 					}
45345 				}
45346 			}
45347 			++n;
45348 		}
45349 		/* Pop arguments, function name from the operand stack and assume the function
45350 		 * does not return anything.
45351 		 */
45352 		jx9MemObjRelease(pTos);
45353 		pTos = &pTos[-pInstr->iP1];
45354 		/* Allocate a new operand stack and evaluate the function body */
45355 		pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode));
45356 		if( pFrameStack == 0 ){
45357 			/* Raise exception: Out of memory */
45358 			VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
45359 				&pVmFunc->sName);
45360 			if( pInstr->iP1 > 0 ){
45361 				VmPopOperand(&pTos, pInstr->iP1);
45362 			}
45363 			break;
45364 		}
45365 		/* Increment nesting level */
45366 		pVm->nRecursionDepth++;
45367 		/* Execute function body */
45368 		rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
45369 		/* Decrement nesting level */
45370 		pVm->nRecursionDepth--;
45371 		/* Free the operand stack */
45372 		SyMemBackendFree(&pVm->sAllocator, pFrameStack);
45373 		/* Leave the frame */
45374 		VmLeaveFrame(&(*pVm));
45375 		if( rc == JX9_ABORT ){
45376 			/* Abort processing immeditaley */
45377 			goto Abort;
45378 		}
45379 	}else{
45380 		jx9_user_func *pFunc;
45381 		jx9_context sCtx;
45382 		jx9_value sRet;
45383 		/* Look for an installed foreign function */
45384 		pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte);
45385 		if( pEntry == 0 ){
45386 			/* Call to undefined function */
45387 			VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName);
45388 			/* Pop given arguments */
45389 			if( pInstr->iP1 > 0 ){
45390 				VmPopOperand(&pTos, pInstr->iP1);
45391 			}
45392 			/* Assume a null return value so that the program continue it's execution normally */
45393 			jx9MemObjRelease(pTos);
45394 			break;
45395 		}
45396 		pFunc = (jx9_user_func *)pEntry->pUserData;
45397 		/* Start collecting function arguments */
45398 		SySetReset(&aArg);
45399 		while( pArg < pTos ){
45400 			SySetPut(&aArg, (const void *)&pArg);
45401 			pArg++;
45402 		}
45403 		/* Assume a null return value */
45404 		jx9MemObjInit(&(*pVm), &sRet);
45405 		/* Init the call context */
45406 		VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
45407 		/* Call the foreign function */
45408 		rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
45409 		/* Release the call context */
45410 		VmReleaseCallContext(&sCtx);
45411 		if( rc == JX9_ABORT ){
45412 			goto Abort;
45413 		}
45414 		if( pInstr->iP1 > 0 ){
45415 			/* Pop function name and arguments */
45416 			VmPopOperand(&pTos, pInstr->iP1);
45417 		}
45418 		/* Save foreign function return value */
45419 		jx9MemObjStore(&sRet, pTos);
45420 		jx9MemObjRelease(&sRet);
45421 	}
45422 	break;
45423 				  }
45424 /*
45425  * OP_CONSUME: P1 * *
45426  * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
45427  */
45428 case JX9_OP_CONSUME: {
45429 	jx9_output_consumer *pCons = &pVm->sVmConsumer;
45430 	jx9_value *pCur, *pOut = pTos;
45431 
45432 	pOut = &pTos[-pInstr->iP1 + 1];
45433 	pCur = pOut;
45434 	/* Start the consume process  */
45435 	while( pOut <= pTos ){
45436 		/* Force a string cast */
45437 		if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){
45438 			jx9MemObjToString(pOut);
45439 		}
45440 		if( SyBlobLength(&pOut->sBlob) > 0 ){
45441 			/*SyBlobNullAppend(&pOut->sBlob);*/
45442 			/* Invoke the output consumer callback */
45443 			rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData);
45444 			/* Increment output length */
45445 			pVm->nOutputLen += SyBlobLength(&pOut->sBlob);
45446 			SyBlobRelease(&pOut->sBlob);
45447 			if( rc == SXERR_ABORT ){
45448 				/* Output consumer callback request an operation abort. */
45449 				goto Abort;
45450 			}
45451 		}
45452 		pOut++;
45453 	}
45454 	pTos = &pCur[-1];
45455 	break;
45456 					 }
45457 
45458 		} /* Switch() */
45459 		pc++; /* Next instruction in the stream */
45460 	} /* For(;;) */
45461 Done:
45462 	SySetRelease(&aArg);
45463 	return SXRET_OK;
45464 Abort:
45465 	SySetRelease(&aArg);
45466 	while( pTos >= pStack ){
45467 		jx9MemObjRelease(pTos);
45468 		pTos--;
45469 	}
45470 	return JX9_ABORT;
45471 }
45472 /*
45473  * Execute as much of a local JX9 bytecode program as we can then return.
45474  * This function is a wrapper around [VmByteCodeExec()].
45475  * See block-comment on that function for additional information.
45476  */
VmLocalExec(jx9_vm * pVm,SySet * pByteCode,jx9_value * pResult)45477 static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
45478 {
45479 	jx9_value *pStack;
45480 	sxi32 rc;
45481 	/* Allocate a new operand stack */
45482 	pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode));
45483 	if( pStack == 0 ){
45484 		return SXERR_MEM;
45485 	}
45486 	/* Execute the program */
45487 	rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult));
45488 	/* Free the operand stack */
45489 	SyMemBackendFree(&pVm->sAllocator, pStack);
45490 	/* Execution result */
45491 	return rc;
45492 }
45493 /*
45494  * Execute as much of a JX9 bytecode program as we can then return.
45495  * This function is a wrapper around [VmByteCodeExec()].
45496  * See block-comment on that function for additional information.
45497  */
jx9VmByteCodeExec(jx9_vm * pVm)45498 JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
45499 {
45500 	/* Make sure we are ready to execute this program */
45501 	if( pVm->nMagic != JX9_VM_RUN ){
45502 		return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
45503 	}
45504 	/* Set the execution magic number  */
45505 	pVm->nMagic = JX9_VM_EXEC;
45506 	/* Execute the program */
45507 	VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
45508 	/*
45509 	 * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
45510 	 * so that any following call to [jx9_vm_exec()] without calling
45511 	 * [jx9_vm_reset()] first would fail.
45512 	 */
45513 	return SXRET_OK;
45514 }
45515 /*
45516  * Extract a memory object (i.e. a variable) from the running script.
45517  * This function must be called after calling jx9_vm_exec(). Otherwise
45518  * NULL is returned.
45519  */
jx9VmExtractVariable(jx9_vm * pVm,SyString * pVar)45520 JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
45521 {
45522 	jx9_value *pValue;
45523 	if( pVm->nMagic != JX9_VM_EXEC ){
45524 		/* call jx9_vm_exec() first */
45525 		return 0;
45526 	}
45527 	/* Perform the lookup */
45528 	pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
45529 	/* Lookup result */
45530 	return pValue;
45531 }
45532 /*
45533  * Invoke the installed VM output consumer callback to consume
45534  * the desired message.
45535  * Refer to the implementation of [jx9_context_output()] defined
45536  * in 'api.c' for additional information.
45537  */
jx9VmOutputConsume(jx9_vm * pVm,SyString * pString)45538 JX9_PRIVATE sxi32 jx9VmOutputConsume(
45539 	jx9_vm *pVm,      /* Target VM */
45540 	SyString *pString /* Message to output */
45541 	)
45542 {
45543 	jx9_output_consumer *pCons = &pVm->sVmConsumer;
45544 	sxi32 rc = SXRET_OK;
45545 	/* Call the output consumer */
45546 	if( pString->nByte > 0 ){
45547 		rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData);
45548 		/* Increment output length */
45549 		pVm->nOutputLen += pString->nByte;
45550 	}
45551 	return rc;
45552 }
45553 /*
45554  * Format a message and invoke the installed VM output consumer
45555  * callback to consume the formatted message.
45556  * Refer to the implementation of [jx9_context_output_format()] defined
45557  * in 'api.c' for additional information.
45558  */
jx9VmOutputConsumeAp(jx9_vm * pVm,const char * zFormat,va_list ap)45559 JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
45560 	jx9_vm *pVm,         /* Target VM */
45561 	const char *zFormat, /* Formatted message to output */
45562 	va_list ap           /* Variable list of arguments */
45563 	)
45564 {
45565 	jx9_output_consumer *pCons = &pVm->sVmConsumer;
45566 	sxi32 rc = SXRET_OK;
45567 	SyBlob sWorker;
45568 	/* Format the message and call the output consumer */
45569 	SyBlobInit(&sWorker, &pVm->sAllocator);
45570 	SyBlobFormatAp(&sWorker, zFormat, ap);
45571 	if( SyBlobLength(&sWorker) > 0 ){
45572 		/* Consume the formatted message */
45573 		rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData);
45574 	}
45575 	/* Increment output length */
45576 	pVm->nOutputLen += SyBlobLength(&sWorker);
45577 	/* Release the working buffer */
45578 	SyBlobRelease(&sWorker);
45579 	return rc;
45580 }
45581 /*
45582  * Return a string representation of the given JX9 OP code.
45583  * This function never fail and always return a pointer
45584  * to a null terminated string.
45585  */
VmInstrToString(sxi32 nOp)45586 static const char * VmInstrToString(sxi32 nOp)
45587 {
45588 	const char *zOp = "Unknown     ";
45589 	switch(nOp){
45590 	case JX9_OP_DONE:       zOp = "DONE       "; break;
45591 	case JX9_OP_HALT:       zOp = "HALT       "; break;
45592 	case JX9_OP_LOAD:       zOp = "LOAD       "; break;
45593 	case JX9_OP_LOADC:      zOp = "LOADC      "; break;
45594 	case JX9_OP_LOAD_MAP:   zOp = "LOAD_MAP   "; break;
45595 	case JX9_OP_LOAD_IDX:   zOp = "LOAD_IDX   "; break;
45596 	case JX9_OP_NOOP:       zOp = "NOOP       "; break;
45597 	case JX9_OP_JMP:        zOp = "JMP        "; break;
45598 	case JX9_OP_JZ:         zOp = "JZ         "; break;
45599 	case JX9_OP_JNZ:        zOp = "JNZ        "; break;
45600 	case JX9_OP_POP:        zOp = "POP        "; break;
45601 	case JX9_OP_CAT:        zOp = "CAT        "; break;
45602 	case JX9_OP_CVT_INT:    zOp = "CVT_INT    "; break;
45603 	case JX9_OP_CVT_STR:    zOp = "CVT_STR    "; break;
45604 	case JX9_OP_CVT_REAL:   zOp = "CVT_REAL   "; break;
45605 	case JX9_OP_CALL:       zOp = "CALL       "; break;
45606 	case JX9_OP_UMINUS:     zOp = "UMINUS     "; break;
45607 	case JX9_OP_UPLUS:      zOp = "UPLUS      "; break;
45608 	case JX9_OP_BITNOT:     zOp = "BITNOT     "; break;
45609 	case JX9_OP_LNOT:       zOp = "LOGNOT     "; break;
45610 	case JX9_OP_MUL:        zOp = "MUL        "; break;
45611 	case JX9_OP_DIV:        zOp = "DIV        "; break;
45612 	case JX9_OP_MOD:        zOp = "MOD        "; break;
45613 	case JX9_OP_ADD:        zOp = "ADD        "; break;
45614 	case JX9_OP_SUB:        zOp = "SUB        "; break;
45615 	case JX9_OP_SHL:        zOp = "SHL        "; break;
45616 	case JX9_OP_SHR:        zOp = "SHR        "; break;
45617 	case JX9_OP_LT:         zOp = "LT         "; break;
45618 	case JX9_OP_LE:         zOp = "LE         "; break;
45619 	case JX9_OP_GT:         zOp = "GT         "; break;
45620 	case JX9_OP_GE:         zOp = "GE         "; break;
45621 	case JX9_OP_EQ:         zOp = "EQ         "; break;
45622 	case JX9_OP_NEQ:        zOp = "NEQ        "; break;
45623 	case JX9_OP_TEQ:        zOp = "TEQ        "; break;
45624 	case JX9_OP_TNE:        zOp = "TNE        "; break;
45625 	case JX9_OP_BAND:       zOp = "BITAND     "; break;
45626 	case JX9_OP_BXOR:       zOp = "BITXOR     "; break;
45627 	case JX9_OP_BOR:        zOp = "BITOR      "; break;
45628 	case JX9_OP_LAND:       zOp = "LOGAND     "; break;
45629 	case JX9_OP_LOR:        zOp = "LOGOR      "; break;
45630 	case JX9_OP_LXOR:       zOp = "LOGXOR     "; break;
45631 	case JX9_OP_STORE:      zOp = "STORE      "; break;
45632 	case JX9_OP_STORE_IDX:  zOp = "STORE_IDX  "; break;
45633 	case JX9_OP_PULL:       zOp = "PULL       "; break;
45634 	case JX9_OP_SWAP:       zOp = "SWAP       "; break;
45635 	case JX9_OP_YIELD:      zOp = "YIELD      "; break;
45636 	case JX9_OP_CVT_BOOL:   zOp = "CVT_BOOL   "; break;
45637 	case JX9_OP_CVT_NULL:   zOp = "CVT_NULL   "; break;
45638 	case JX9_OP_CVT_ARRAY:  zOp = "CVT_JSON   "; break;
45639 	case JX9_OP_CVT_NUMC:   zOp = "CVT_NUMC   "; break;
45640 	case JX9_OP_INCR:       zOp = "INCR       "; break;
45641 	case JX9_OP_DECR:       zOp = "DECR       "; break;
45642 	case JX9_OP_ADD_STORE:  zOp = "ADD_STORE  "; break;
45643 	case JX9_OP_SUB_STORE:  zOp = "SUB_STORE  "; break;
45644 	case JX9_OP_MUL_STORE:  zOp = "MUL_STORE  "; break;
45645 	case JX9_OP_DIV_STORE:  zOp = "DIV_STORE  "; break;
45646 	case JX9_OP_MOD_STORE:  zOp = "MOD_STORE  "; break;
45647 	case JX9_OP_CAT_STORE:  zOp = "CAT_STORE  "; break;
45648 	case JX9_OP_SHL_STORE:  zOp = "SHL_STORE  "; break;
45649 	case JX9_OP_SHR_STORE:  zOp = "SHR_STORE  "; break;
45650 	case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
45651 	case JX9_OP_BOR_STORE:  zOp = "BOR_STORE  "; break;
45652 	case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
45653 	case JX9_OP_CONSUME:    zOp = "CONSUME    "; break;
45654 	case JX9_OP_MEMBER:     zOp = "MEMBER     "; break;
45655 	case JX9_OP_UPLINK:     zOp = "UPLINK     "; break;
45656 	case JX9_OP_SWITCH:     zOp = "SWITCH     "; break;
45657 	case JX9_OP_FOREACH_INIT:
45658 		                    zOp = "4EACH_INIT "; break;
45659 	case JX9_OP_FOREACH_STEP:
45660 						    zOp = "4EACH_STEP "; break;
45661 	default:
45662 		break;
45663 	}
45664 	return zOp;
45665 }
45666 /*
45667  * Dump JX9 bytecodes instructions to a human readable format.
45668  * The xConsumer() callback which is an used defined function
45669  * is responsible of consuming the generated dump.
45670  */
jx9VmDump(jx9_vm * pVm,ProcConsumer xConsumer,void * pUserData)45671 JX9_PRIVATE sxi32 jx9VmDump(
45672 	jx9_vm *pVm,            /* Target VM */
45673 	ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
45674 	void *pUserData         /* Last argument to xConsumer() */
45675 	)
45676 {
45677 	sxi32 rc;
45678 	rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData);
45679 	return rc;
45680 }
45681 /*
45682  * Default constant expansion callback used by the 'const' statement if used
45683  * outside a object body [i.e: global or function scope].
45684  * Refer to the implementation of [JX9_CompileConstant()] defined
45685  * in 'compile.c' for additional information.
45686  */
jx9VmExpandConstantValue(jx9_value * pVal,void * pUserData)45687 JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
45688 {
45689 	SySet *pByteCode = (SySet *)pUserData;
45690 	/* Evaluate and expand constant value */
45691 	VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
45692 }
45693 /*
45694  * Section:
45695  *  Function handling functions.
45696  * Authors:
45697  *    Symisc Systems, devel@symisc.net.
45698  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
45699  * Status:
45700  *    Stable.
45701  */
45702 /*
45703  * int func_num_args(void)
45704  *   Returns the number of arguments passed to the function.
45705  * Parameters
45706  *   None.
45707  * Return
45708  *  Total number of arguments passed into the current user-defined function
45709  *  or -1 if called from the globe scope.
45710  */
vm_builtin_func_num_args(jx9_context * pCtx,int nArg,jx9_value ** apArg)45711 static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
45712 {
45713 	VmFrame *pFrame;
45714 	jx9_vm *pVm;
45715 	/* Point to the target VM */
45716 	pVm = pCtx->pVm;
45717 	/* Current frame */
45718 	pFrame = pVm->pFrame;
45719 	if( pFrame->pParent == 0 ){
45720 		SXUNUSED(nArg);
45721 		SXUNUSED(apArg);
45722 		/* Global frame, return -1 */
45723 		jx9_result_int(pCtx, -1);
45724 		return SXRET_OK;
45725 	}
45726 	/* Total number of arguments passed to the enclosing function */
45727 	nArg = (int)SySetUsed(&pFrame->sArg);
45728 	jx9_result_int(pCtx, nArg);
45729 	return SXRET_OK;
45730 }
45731 /*
45732  * value func_get_arg(int $arg_num)
45733  *   Return an item from the argument list.
45734  * Parameters
45735  *  Argument number(index start from zero).
45736  * Return
45737  *  Returns the specified argument or FALSE on error.
45738  */
vm_builtin_func_get_arg(jx9_context * pCtx,int nArg,jx9_value ** apArg)45739 static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg)
45740 {
45741 	jx9_value *pObj = 0;
45742 	VmSlot *pSlot = 0;
45743 	VmFrame *pFrame;
45744 	jx9_vm *pVm;
45745 	/* Point to the target VM */
45746 	pVm = pCtx->pVm;
45747 	/* Current frame */
45748 	pFrame = pVm->pFrame;
45749 	if( nArg < 1 || pFrame->pParent == 0 ){
45750 		/* Global frame or Missing arguments, return FALSE */
45751 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
45752 		jx9_result_bool(pCtx, 0);
45753 		return SXRET_OK;
45754 	}
45755 	/* Extract the desired index */
45756 	nArg = jx9_value_to_int(apArg[0]);
45757 	if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
45758 		/* Invalid index, return FALSE */
45759 		jx9_result_bool(pCtx, 0);
45760 		return SXRET_OK;
45761 	}
45762 	/* Extract the desired argument */
45763 	if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
45764 		if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
45765 			/* Return the desired argument */
45766 			jx9_result_value(pCtx, (jx9_value *)pObj);
45767 		}else{
45768 			/* No such argument, return false */
45769 			jx9_result_bool(pCtx, 0);
45770 		}
45771 	}else{
45772 		/* CAN'T HAPPEN */
45773 		jx9_result_bool(pCtx, 0);
45774 	}
45775 	return SXRET_OK;
45776 }
45777 /*
45778  * array func_get_args(void)
45779  *   Returns an array comprising a copy of function's argument list.
45780  * Parameters
45781  *  None.
45782  * Return
45783  *  Returns an array in which each element is a copy of the corresponding
45784  *  member of the current user-defined function's argument list.
45785  *  Otherwise FALSE is returned on failure.
45786  */
vm_builtin_func_get_args(jx9_context * pCtx,int nArg,jx9_value ** apArg)45787 static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
45788 {
45789 	jx9_value *pObj = 0;
45790 	jx9_value *pArray;
45791 	VmFrame *pFrame;
45792 	VmSlot *aSlot;
45793 	sxu32 n;
45794 	/* Point to the current frame */
45795 	pFrame = pCtx->pVm->pFrame;
45796 	if( pFrame->pParent == 0 ){
45797 		/* Global frame, return FALSE */
45798 		jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
45799 		jx9_result_bool(pCtx, 0);
45800 		return SXRET_OK;
45801 	}
45802 	/* Create a new array */
45803 	pArray = jx9_context_new_array(pCtx);
45804 	if( pArray == 0 ){
45805 		SXUNUSED(nArg); /* cc warning */
45806 		SXUNUSED(apArg);
45807 		jx9_result_bool(pCtx, 0);
45808 		return SXRET_OK;
45809 	}
45810 	/* Start filling the array with the given arguments */
45811 	aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
45812 	for( n = 0;  n < SySetUsed(&pFrame->sArg) ; n++ ){
45813 		pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
45814 		if( pObj ){
45815 			jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
45816 		}
45817 	}
45818 	/* Return the freshly created array */
45819 	jx9_result_value(pCtx, pArray);
45820 	return SXRET_OK;
45821 }
45822 /*
45823  * bool function_exists(string $name)
45824  *  Return TRUE if the given function has been defined.
45825  * Parameters
45826  *  The name of the desired function.
45827  * Return
45828  *  Return TRUE if the given function has been defined.False otherwise
45829  */
vm_builtin_func_exists(jx9_context * pCtx,int nArg,jx9_value ** apArg)45830 static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
45831 {
45832 	const char *zName;
45833 	jx9_vm *pVm;
45834 	int nLen;
45835 	int res;
45836 	if( nArg < 1 ){
45837 		/* Missing argument, return FALSE */
45838 		jx9_result_bool(pCtx, 0);
45839 		return SXRET_OK;
45840 	}
45841 	/* Point to the target VM */
45842 	pVm = pCtx->pVm;
45843 	/* Extract the function name */
45844 	zName = jx9_value_to_string(apArg[0], &nLen);
45845 	/* Assume the function is not defined */
45846 	res = 0;
45847 	/* Perform the lookup */
45848 	if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
45849 		SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
45850 			/* Function is defined */
45851 			res = 1;
45852 	}
45853 	jx9_result_bool(pCtx, res);
45854 	return SXRET_OK;
45855 }
45856 /*
45857  * Verify that the contents of a variable can be called as a function.
45858  * [i.e: Whether it is callable or not].
45859  * Return TRUE if callable.FALSE otherwise.
45860  */
jx9VmIsCallable(jx9_vm * pVm,jx9_value * pValue)45861 JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
45862 {
45863 	int res = 0;
45864 	if( pValue->iFlags & MEMOBJ_STRING ){
45865 		const char *zName;
45866 		int nLen;
45867 		/* Extract the name */
45868 		zName = jx9_value_to_string(pValue, &nLen);
45869 		/* Perform the lookup */
45870 		if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
45871 			SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
45872 				/* Function is callable */
45873 				res = 1;
45874 		}
45875 	}
45876 	return res;
45877 }
45878 /*
45879  * bool is_callable(callable $name[, bool $syntax_only = false])
45880  * Verify that the contents of a variable can be called as a function.
45881  * Parameters
45882  * $name
45883  *    The callback function to check
45884  * $syntax_only
45885  *    If set to TRUE the function only verifies that name might be a function or method.
45886  *    It will only reject simple variables that are not strings, or an array that does
45887  *    not have a valid structure to be used as a callback. The valid ones are supposed
45888  *    to have only 2 entries, the first of which is an object or a string, and the second
45889  *    a string.
45890  * Return
45891  *  TRUE if name is callable, FALSE otherwise.
45892  */
vm_builtin_is_callable(jx9_context * pCtx,int nArg,jx9_value ** apArg)45893 static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg)
45894 {
45895 	jx9_vm *pVm;
45896 	int res;
45897 	if( nArg < 1 ){
45898 		/* Missing arguments, return FALSE */
45899 		jx9_result_bool(pCtx, 0);
45900 		return SXRET_OK;
45901 	}
45902 	/* Point to the target VM */
45903 	pVm = pCtx->pVm;
45904 	/* Perform the requested operation */
45905 	res = jx9VmIsCallable(pVm, apArg[0]);
45906 	jx9_result_bool(pCtx, res);
45907 	return SXRET_OK;
45908 }
45909 /*
45910  * Hash walker callback used by the [get_defined_functions()] function
45911  * defined below.
45912  */
VmHashFuncStep(SyHashEntry * pEntry,void * pUserData)45913 static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData)
45914 {
45915 	jx9_value *pArray = (jx9_value *)pUserData;
45916 	jx9_value sName;
45917 	sxi32 rc;
45918 	/* Prepare the function name for insertion */
45919 	jx9MemObjInitFromString(pArray->pVm, &sName, 0);
45920 	jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
45921 	/* Perform the insertion */
45922 	rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
45923 	jx9MemObjRelease(&sName);
45924 	return rc;
45925 }
45926 /*
45927  * array get_defined_functions(void)
45928  *  Returns an array of all defined functions.
45929  * Parameter
45930  *  None.
45931  * Return
45932  *  Returns an multidimensional array containing a list of all defined functions
45933  *  both built-in (internal) and user-defined.
45934  *  The internal functions will be accessible via $arr["internal"], and the user
45935  *  defined ones using $arr["user"].
45936  * Note:
45937  *  NULL is returned on failure.
45938  */
vm_builtin_get_defined_func(jx9_context * pCtx,int nArg,jx9_value ** apArg)45939 static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg)
45940 {
45941 	jx9_value *pArray;
45942 	/* NOTE:
45943 	 * Don't worry about freeing memory here, every allocated resource will be released
45944 	 * automatically by the engine as soon we return from this foreign function.
45945 	 */
45946 	pArray = jx9_context_new_array(pCtx);
45947  	if( pArray == 0 ){
45948 		SXUNUSED(nArg); /* cc warning */
45949 		SXUNUSED(apArg);
45950 		/* Return NULL */
45951 		jx9_result_null(pCtx);
45952 		return SXRET_OK;
45953 	}
45954 	/* Fill with the appropriate information */
45955 	SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
45956 	/* Fill with the appropriate information */
45957 	SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
45958 	/* Return a copy of the array array */
45959 	jx9_result_value(pCtx, pArray);
45960 	return SXRET_OK;
45961 }
45962 /*
45963  * Call a user defined or foreign function where the name of the function
45964  * is stored in the pFunc parameter and the given arguments are stored
45965  * in the apArg[] array.
45966  * Return SXRET_OK if the function was successfuly called.Any other
45967  * return value indicates failure.
45968  */
jx9VmCallUserFunction(jx9_vm * pVm,jx9_value * pFunc,int nArg,jx9_value ** apArg,jx9_value * pResult)45969 JX9_PRIVATE sxi32 jx9VmCallUserFunction(
45970 	jx9_vm *pVm,       /* Target VM */
45971 	jx9_value *pFunc,  /* Callback name */
45972 	int nArg,          /* Total number of given arguments */
45973 	jx9_value **apArg, /* Callback arguments */
45974 	jx9_value *pResult /* Store callback return value here. NULL otherwise */
45975 	)
45976 {
45977 	jx9_value *aStack;
45978 	VmInstr aInstr[2];
45979 	int i;
45980 	if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
45981 		/* Don't bother processing, it's invalid anyway */
45982 		if( pResult ){
45983 			/* Assume a null return value */
45984 			jx9MemObjRelease(pResult);
45985 		}
45986 		return SXERR_INVALID;
45987 	}
45988 	/* Create a new operand stack */
45989 	aStack = VmNewOperandStack(&(*pVm), 1+nArg);
45990 	if( aStack == 0 ){
45991 		jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
45992 			"JX9 is running out of memory while invoking user callback");
45993 		if( pResult ){
45994 			/* Assume a null return value */
45995 			jx9MemObjRelease(pResult);
45996 		}
45997 		return SXERR_MEM;
45998 	}
45999 	/* Fill the operand stack with the given arguments */
46000 	for( i = 0 ; i < nArg ; i++ ){
46001 		jx9MemObjLoad(apArg[i], &aStack[i]);
46002 		aStack[i].nIdx = apArg[i]->nIdx;
46003 	}
46004 	/* Push the function name */
46005 	jx9MemObjLoad(pFunc, &aStack[i]);
46006 	aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
46007 	/* Emit the CALL istruction */
46008 	aInstr[0].iOp = JX9_OP_CALL;
46009 	aInstr[0].iP1 = nArg; /* Total number of given arguments */
46010 	aInstr[0].iP2 = 0;
46011 	aInstr[0].p3  = 0;
46012 	/* Emit the DONE instruction */
46013 	aInstr[1].iOp = JX9_OP_DONE;
46014 	aInstr[1].iP1 = 1;   /* Extract function return value if available */
46015 	aInstr[1].iP2 = 0;
46016 	aInstr[1].p3  = 0;
46017 	/* Execute the function body (if available) */
46018 	VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult);
46019 	/* Clean up the mess left behind */
46020 	SyMemBackendFree(&pVm->sAllocator, aStack);
46021 	return JX9_OK;
46022 }
46023 /*
46024  * Call a user defined or foreign function whith a varibale number
46025  * of arguments where the name of the function is stored in the pFunc
46026  * parameter.
46027  * Return SXRET_OK if the function was successfuly called.Any other
46028  * return value indicates failure.
46029  */
jx9VmCallUserFunctionAp(jx9_vm * pVm,jx9_value * pFunc,jx9_value * pResult,...)46030 JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
46031 	jx9_vm *pVm,       /* Target VM */
46032 	jx9_value *pFunc,  /* Callback name */
46033 	jx9_value *pResult, /* Store callback return value here. NULL otherwise */
46034 	...                /* 0 (Zero) or more Callback arguments */
46035 	)
46036 {
46037 	jx9_value *pArg;
46038 	SySet aArg;
46039 	va_list ap;
46040 	sxi32 rc;
46041 	SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
46042 	/* Copy arguments one after one */
46043 	va_start(ap, pResult);
46044 	for(;;){
46045 		pArg = va_arg(ap, jx9_value *);
46046 		if( pArg == 0 ){
46047 			break;
46048 		}
46049 		SySetPut(&aArg, (const void *)&pArg);
46050 	}
46051 	/* Call the core routine */
46052 	rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
46053 	/* Cleanup */
46054 	SySetRelease(&aArg);
46055 	return rc;
46056 }
46057 /*
46058  * bool defined(string $name)
46059  *  Checks whether a given named constant exists.
46060  * Parameter:
46061  *  Name of the desired constant.
46062  * Return
46063  *  TRUE if the given constant exists.FALSE otherwise.
46064  */
vm_builtin_defined(jx9_context * pCtx,int nArg,jx9_value ** apArg)46065 static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg)
46066 {
46067 	const char *zName;
46068 	int nLen = 0;
46069 	int res = 0;
46070 	if( nArg < 1 ){
46071 		/* Missing constant name, return FALSE */
46072 		jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
46073 		jx9_result_bool(pCtx, 0);
46074 		return SXRET_OK;
46075 	}
46076 	/* Extract constant name */
46077 	zName = jx9_value_to_string(apArg[0], &nLen);
46078 	/* Perform the lookup */
46079 	if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){
46080 		/* Already defined */
46081 		res = 1;
46082 	}
46083 	jx9_result_bool(pCtx, res);
46084 	return SXRET_OK;
46085 }
46086 /*
46087  * Hash walker callback used by the [get_defined_constants()] function
46088  * defined below.
46089  */
VmHashConstStep(SyHashEntry * pEntry,void * pUserData)46090 static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
46091 {
46092 	jx9_value *pArray = (jx9_value *)pUserData;
46093 	jx9_value sName;
46094 	sxi32 rc;
46095 	/* Prepare the constant name for insertion */
46096 	jx9MemObjInitFromString(pArray->pVm, &sName, 0);
46097 	jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
46098 	/* Perform the insertion */
46099 	rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
46100 	jx9MemObjRelease(&sName);
46101 	return rc;
46102 }
46103 /*
46104  * array get_defined_constants(void)
46105  *  Returns an associative array with the names of all defined
46106  *  constants.
46107  * Parameters
46108  *  NONE.
46109  * Returns
46110  *  Returns the names of all the constants currently defined.
46111  */
vm_builtin_get_defined_constants(jx9_context * pCtx,int nArg,jx9_value ** apArg)46112 static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg)
46113 {
46114 	jx9_value *pArray;
46115 	/* Create the array first*/
46116 	pArray = jx9_context_new_array(pCtx);
46117 	if( pArray == 0 ){
46118 		SXUNUSED(nArg); /* cc warning */
46119 		SXUNUSED(apArg);
46120 		/* Return NULL */
46121 		jx9_result_null(pCtx);
46122 		return SXRET_OK;
46123 	}
46124 	/* Fill the array with the defined constants */
46125 	SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
46126 	/* Return the created array */
46127 	jx9_result_value(pCtx, pArray);
46128 	return SXRET_OK;
46129 }
46130 /*
46131  * Section:
46132  *  Random numbers/string generators.
46133  * Authors:
46134  *    Symisc Systems, devel@symisc.net.
46135  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
46136  * Status:
46137  *    Stable.
46138  */
46139 /*
46140  * Generate a random 32-bit unsigned integer.
46141  * JX9 use it's own private PRNG which is based on the one
46142  * used by te SQLite3 library.
46143  */
jx9VmRandomNum(jx9_vm * pVm)46144 JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm)
46145 {
46146 	sxu32 iNum;
46147 	SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32));
46148 	return iNum;
46149 }
46150 /*
46151  * Generate a random string (English Alphabet) of length nLen.
46152  * Note that the generated string is NOT null terminated.
46153  * JX9 use it's own private PRNG which is based on the one used
46154  * by te SQLite3 library.
46155  */
jx9VmRandomString(jx9_vm * pVm,char * zBuf,int nLen)46156 JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen)
46157 {
46158 	static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
46159 	int i;
46160 	/* Generate a binary string first */
46161 	SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen);
46162 	/* Turn the binary string into english based alphabet */
46163 	for( i = 0 ; i < nLen ; ++i ){
46164 		 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
46165 	 }
46166 }
46167 /*
46168  * int rand()
46169  *  Generate a random (unsigned 32-bit) integer.
46170  * Parameter
46171  *  $min
46172  *    The lowest value to return (default: 0)
46173  *  $max
46174  *   The highest value to return (default: getrandmax())
46175  * Return
46176  *   A pseudo random value between min (or 0) and max (or getrandmax(), inclusive).
46177  * Note:
46178  *  JX9 use it's own private PRNG which is based on the one used
46179  *  by te SQLite3 library.
46180  */
vm_builtin_rand(jx9_context * pCtx,int nArg,jx9_value ** apArg)46181 static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
46182 {
46183 	sxu32 iNum;
46184 	/* Generate the random number */
46185 	iNum = jx9VmRandomNum(pCtx->pVm);
46186 	if( nArg > 1 ){
46187 		sxu32 iMin, iMax;
46188 		iMin = (sxu32)jx9_value_to_int(apArg[0]);
46189 		iMax = (sxu32)jx9_value_to_int(apArg[1]);
46190 		if( iMin < iMax ){
46191 			sxu32 iDiv = iMax+1-iMin;
46192 			if( iDiv > 0 ){
46193 				iNum = (iNum % iDiv)+iMin;
46194 			}
46195 		}else if(iMax > 0 ){
46196 			iNum %= iMax;
46197 		}
46198 	}
46199 	/* Return the number */
46200 	jx9_result_int64(pCtx, (jx9_int64)iNum);
46201 	return SXRET_OK;
46202 }
46203 /*
46204  * int getrandmax(void)
46205  *   Show largest possible random value
46206  * Return
46207  *  The largest possible random value returned by rand() which is in
46208  *  this implementation 0xFFFFFFFF.
46209  * Note:
46210  *  JX9 use it's own private PRNG which is based on the one used
46211  *  by te SQLite3 library.
46212  */
vm_builtin_getrandmax(jx9_context * pCtx,int nArg,jx9_value ** apArg)46213 static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
46214 {
46215 	SXUNUSED(nArg); /* cc warning */
46216 	SXUNUSED(apArg);
46217 	jx9_result_int64(pCtx, SXU32_HIGH);
46218 	return SXRET_OK;
46219 }
46220 /*
46221  * string rand_str()
46222  * string rand_str(int $len)
46223  *  Generate a random string (English alphabet).
46224  * Parameter
46225  *  $len
46226  *    Length of the desired string (default: 16, Min: 1, Max: 1024)
46227  * Return
46228  *   A pseudo random string.
46229  * Note:
46230  *  JX9 use it's own private PRNG which is based on the one used
46231  *  by te SQLite3 library.
46232  */
vm_builtin_rand_str(jx9_context * pCtx,int nArg,jx9_value ** apArg)46233 static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
46234 {
46235 	char zString[1024];
46236 	int iLen = 0x10;
46237 	if( nArg > 0 ){
46238 		/* Get the desired length */
46239 		iLen = jx9_value_to_int(apArg[0]);
46240 		if( iLen < 1 || iLen > 1024 ){
46241 			/* Default length */
46242 			iLen = 0x10;
46243 		}
46244 	}
46245 	/* Generate the random string */
46246 	jx9VmRandomString(pCtx->pVm, zString, iLen);
46247 	/* Return the generated string */
46248 	jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
46249 	return SXRET_OK;
46250 }
46251 /*
46252  * Section:
46253  *  Language construct implementation as foreign functions.
46254  * Authors:
46255  *    Symisc Systems, devel@symisc.net.
46256  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
46257  * Status:
46258  *    Stable.
46259  */
46260 /*
46261  * void print($string...)
46262  *  Output one or more messages.
46263  * Parameters
46264  *  $string
46265  *   Message to output.
46266  * Return
46267  *  NULL.
46268  */
vm_builtin_print(jx9_context * pCtx,int nArg,jx9_value ** apArg)46269 static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
46270 {
46271 	const char *zData;
46272 	int nDataLen = 0;
46273 	jx9_vm *pVm;
46274 	int i, rc;
46275 	/* Point to the target VM */
46276 	pVm = pCtx->pVm;
46277 	/* Output */
46278 	for( i = 0 ; i < nArg ; ++i ){
46279 		zData = jx9_value_to_string(apArg[i], &nDataLen);
46280 		if( nDataLen > 0 ){
46281 			rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
46282 			/* Increment output length */
46283 			pVm->nOutputLen += nDataLen;
46284 			if( rc == SXERR_ABORT ){
46285 				/* Output consumer callback request an operation abort */
46286 				return JX9_ABORT;
46287 			}
46288 		}
46289 	}
46290 	return SXRET_OK;
46291 }
46292 /*
46293  * void exit(string $msg)
46294  * void exit(int $status)
46295  * void die(string $ms)
46296  * void die(int $status)
46297  *   Output a message and terminate program execution.
46298  * Parameter
46299  *  If status is a string, this function prints the status just before exiting.
46300  *  If status is an integer, that value will be used as the exit status
46301  *  and not printed
46302  * Return
46303  *  NULL
46304  */
vm_builtin_exit(jx9_context * pCtx,int nArg,jx9_value ** apArg)46305 static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg)
46306 {
46307 	if( nArg > 0 ){
46308 		if( jx9_value_is_string(apArg[0]) ){
46309 			const char *zData;
46310 			int iLen = 0;
46311 			/* Print exit message */
46312 			zData = jx9_value_to_string(apArg[0], &iLen);
46313 			jx9_context_output(pCtx, zData, iLen);
46314 		}else if(jx9_value_is_int(apArg[0]) ){
46315 			sxi32 iExitStatus;
46316 			/* Record exit status code */
46317 			iExitStatus = jx9_value_to_int(apArg[0]);
46318 			pCtx->pVm->iExitStatus = iExitStatus;
46319 		}
46320 	}
46321 	/* Abort processing immediately */
46322 	return JX9_ABORT;
46323 }
46324 /*
46325  * Unset a memory object [i.e: a jx9_value].
46326  */
jx9VmUnsetMemObj(jx9_vm * pVm,sxu32 nObjIdx)46327 JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
46328 {
46329 	jx9_value *pObj;
46330 	pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
46331 	if( pObj ){
46332 		VmSlot sFree;
46333 		/* Release the object */
46334 		jx9MemObjRelease(pObj);
46335 		/* Restore to the free list */
46336 		sFree.nIdx = nObjIdx;
46337 		sFree.pUserData = 0;
46338 		SySetPut(&pVm->aFreeObj, (const void *)&sFree);
46339 	}
46340 	return SXRET_OK;
46341 }
46342 /*
46343  * string gettype($var)
46344  *  Get the type of a variable
46345  * Parameters
46346  *   $var
46347  *    The variable being type checked.
46348  * Return
46349  *   String representation of the given variable type.
46350  */
vm_builtin_gettype(jx9_context * pCtx,int nArg,jx9_value ** apArg)46351 static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg)
46352 {
46353 	const char *zType = "null";
46354 	if( nArg > 0 ){
46355 		zType = jx9MemObjTypeDump(apArg[0]);
46356 	}
46357 	/* Return the variable type */
46358 	jx9_result_string(pCtx, zType, -1/*Compute length automatically*/);
46359 	return SXRET_OK;
46360 }
46361 /*
46362  * string get_resource_type(resource $handle)
46363  *  This function gets the type of the given resource.
46364  * Parameters
46365  *  $handle
46366  *  The evaluated resource handle.
46367  * Return
46368  *  If the given handle is a resource, this function will return a string
46369  *  representing its type. If the type is not identified by this function
46370  *  the return value will be the string Unknown.
46371  *  This function will return FALSE and generate an error if handle
46372  *  is not a resource.
46373  */
vm_builtin_get_resource_type(jx9_context * pCtx,int nArg,jx9_value ** apArg)46374 static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg)
46375 {
46376 	if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
46377 		/* Missing/Invalid arguments, return FALSE*/
46378 		jx9_result_bool(pCtx, 0);
46379 		return SXRET_OK;
46380 	}
46381 	jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
46382 	return SXRET_OK;
46383 }
46384 /*
46385  * void dump(expression, ....)
46386  *   dump — Dumps information about a variable
46387  * Parameters
46388  *   One or more expression to dump.
46389  * Returns
46390  *  Nothing.
46391  */
vm_builtin_dump(jx9_context * pCtx,int nArg,jx9_value ** apArg)46392 static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg)
46393 {
46394 	SyBlob sDump; /* Generated dump is stored here */
46395 	int i;
46396 	SyBlobInit(&sDump,&pCtx->pVm->sAllocator);
46397 	/* Dump one or more expressions */
46398 	for( i = 0 ; i < nArg ; i++ ){
46399 		jx9_value *pObj = apArg[i];
46400 		/* Reset the working buffer */
46401 		SyBlobReset(&sDump);
46402 		/* Dump the given expression */
46403 		jx9MemObjDump(&sDump,pObj);
46404 		/* Output */
46405 		if( SyBlobLength(&sDump) > 0 ){
46406 			jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
46407 		}
46408 	}
46409 	/* Release the working buffer */
46410 	SyBlobRelease(&sDump);
46411 	return SXRET_OK;
46412 }
46413 /*
46414  * Section:
46415  *  Version, Credits and Copyright related functions.
46416  * Authors:
46417  *    Symisc Systems, devel@symisc.net.
46418  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
46419  *    Stable.
46420  */
46421 /*
46422  * string jx9_version(void)
46423  * string jx9_credits(void)
46424  *  Returns the running version of the jx9 version.
46425  * Parameters
46426  *  None
46427  * Return
46428  * Current jx9 version.
46429  */
vm_builtin_jx9_version(jx9_context * pCtx,int nArg,jx9_value ** apArg)46430 static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
46431 {
46432 	SXUNUSED(nArg);
46433 	SXUNUSED(apArg); /* cc warning */
46434 	/* Current engine version, signature and cipyright notice */
46435 	jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
46436 	return JX9_OK;
46437 }
46438 /*
46439  * Section:
46440  *    URL related routines.
46441  * Authors:
46442  *    Symisc Systems, devel@symisc.net.
46443  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
46444  * Status:
46445  *    Stable.
46446  */
46447 /* Forward declaration */
46448 static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen);
46449 /*
46450  * value parse_url(string $url [, int $component = -1 ])
46451  *  Parse a URL and return its fields.
46452  * Parameters
46453  *  $url
46454  *   The URL to parse.
46455  * $component
46456  *  Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
46457  *  JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
46458  *  just a specific URL component as a string (except when JX9_URL_PORT is given
46459  *  in which case the return value will be an integer).
46460  * Return
46461  *  If the component parameter is omitted, an associative array is returned.
46462  *  At least one element will be present within the array. Potential keys within
46463  *  this array are:
46464  *   scheme - e.g. http
46465  *   host
46466  *   port
46467  *   user
46468  *   pass
46469  *   path
46470  *   query - after the question mark ?
46471  *   fragment - after the hashmark #
46472  * Note:
46473  *  FALSE is returned on failure.
46474  *  This function work with relative URL unlike the one shipped
46475  *  with the standard JX9 engine.
46476  */
vm_builtin_parse_url(jx9_context * pCtx,int nArg,jx9_value ** apArg)46477 static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg)
46478 {
46479 	const char *zStr; /* Input string */
46480 	SyString *pComp;  /* Pointer to the URI component */
46481 	SyhttpUri sURI;   /* Parse of the given URI */
46482 	int nLen;
46483 	sxi32 rc;
46484 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
46485 		/* Missing/Invalid arguments, return FALSE */
46486 		jx9_result_bool(pCtx, 0);
46487 		return JX9_OK;
46488 	}
46489 	/* Extract the given URI */
46490 	zStr = jx9_value_to_string(apArg[0], &nLen);
46491 	if( nLen < 1 ){
46492 		/* Nothing to process, return FALSE */
46493 		jx9_result_bool(pCtx, 0);
46494 		return JX9_OK;
46495 	}
46496 	/* Get a parse */
46497 	rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
46498 	if( rc != SXRET_OK ){
46499 		/* Malformed input, return FALSE */
46500 		jx9_result_bool(pCtx, 0);
46501 		return JX9_OK;
46502 	}
46503 	if( nArg > 1 ){
46504 		int nComponent = jx9_value_to_int(apArg[1]);
46505 		/* Refer to constant.c for constants values */
46506 		switch(nComponent){
46507 		case 1: /* JX9_URL_SCHEME */
46508 			pComp = &sURI.sScheme;
46509 			if( pComp->nByte < 1 ){
46510 				/* No available value, return NULL */
46511 				jx9_result_null(pCtx);
46512 			}else{
46513 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46514 			}
46515 			break;
46516 		case 2: /* JX9_URL_HOST */
46517 			pComp = &sURI.sHost;
46518 			if( pComp->nByte < 1 ){
46519 				/* No available value, return NULL */
46520 				jx9_result_null(pCtx);
46521 			}else{
46522 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46523 			}
46524 			break;
46525 		case 3: /* JX9_URL_PORT */
46526 			pComp = &sURI.sPort;
46527 			if( pComp->nByte < 1 ){
46528 				/* No available value, return NULL */
46529 				jx9_result_null(pCtx);
46530 			}else{
46531 				int iPort = 0;
46532 				/* Cast the value to integer */
46533 				SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
46534 				jx9_result_int(pCtx, iPort);
46535 			}
46536 			break;
46537 		case 4: /* JX9_URL_USER */
46538 			pComp = &sURI.sUser;
46539 			if( pComp->nByte < 1 ){
46540 				/* No available value, return NULL */
46541 				jx9_result_null(pCtx);
46542 			}else{
46543 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46544 			}
46545 			break;
46546 		case 5: /* JX9_URL_PASS */
46547 			pComp = &sURI.sPass;
46548 			if( pComp->nByte < 1 ){
46549 				/* No available value, return NULL */
46550 				jx9_result_null(pCtx);
46551 			}else{
46552 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46553 			}
46554 			break;
46555 		case 7: /* JX9_URL_QUERY */
46556 			pComp = &sURI.sQuery;
46557 			if( pComp->nByte < 1 ){
46558 				/* No available value, return NULL */
46559 				jx9_result_null(pCtx);
46560 			}else{
46561 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46562 			}
46563 			break;
46564 		case 8: /* JX9_URL_FRAGMENT */
46565 			pComp = &sURI.sFragment;
46566 			if( pComp->nByte < 1 ){
46567 				/* No available value, return NULL */
46568 				jx9_result_null(pCtx);
46569 			}else{
46570 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46571 			}
46572 			break;
46573 		case 6: /*  JX9_URL_PATH */
46574 			pComp = &sURI.sPath;
46575 			if( pComp->nByte < 1 ){
46576 				/* No available value, return NULL */
46577 				jx9_result_null(pCtx);
46578 			}else{
46579 				jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
46580 			}
46581 			break;
46582 		default:
46583 			/* No such entry, return NULL */
46584 			jx9_result_null(pCtx);
46585 			break;
46586 		}
46587 	}else{
46588 		jx9_value *pArray, *pValue;
46589 		/* Return an associative array */
46590 		pArray = jx9_context_new_array(pCtx);  /* Empty array */
46591 		pValue = jx9_context_new_scalar(pCtx); /* Array value */
46592 		if( pArray == 0 || pValue == 0 ){
46593 			/* Out of memory */
46594 			jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
46595 			/* Return false */
46596 			jx9_result_bool(pCtx, 0);
46597 			return JX9_OK;
46598 		}
46599 		/* Fill the array */
46600 		pComp = &sURI.sScheme;
46601 		if( pComp->nByte > 0 ){
46602 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46603 			jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
46604 		}
46605 		/* Reset the string cursor */
46606 		jx9_value_reset_string_cursor(pValue);
46607 		pComp = &sURI.sHost;
46608 		if( pComp->nByte > 0 ){
46609 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46610 			jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
46611 		}
46612 		/* Reset the string cursor */
46613 		jx9_value_reset_string_cursor(pValue);
46614 		pComp = &sURI.sPort;
46615 		if( pComp->nByte > 0 ){
46616 			int iPort = 0;/* cc warning */
46617 			/* Convert to integer */
46618 			SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
46619 			jx9_value_int(pValue, iPort);
46620 			jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
46621 		}
46622 		/* Reset the string cursor */
46623 		jx9_value_reset_string_cursor(pValue);
46624 		pComp = &sURI.sUser;
46625 		if( pComp->nByte > 0 ){
46626 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46627 			jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
46628 		}
46629 		/* Reset the string cursor */
46630 		jx9_value_reset_string_cursor(pValue);
46631 		pComp = &sURI.sPass;
46632 		if( pComp->nByte > 0 ){
46633 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46634 			jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
46635 		}
46636 		/* Reset the string cursor */
46637 		jx9_value_reset_string_cursor(pValue);
46638 		pComp = &sURI.sPath;
46639 		if( pComp->nByte > 0 ){
46640 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46641 			jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
46642 		}
46643 		/* Reset the string cursor */
46644 		jx9_value_reset_string_cursor(pValue);
46645 		pComp = &sURI.sQuery;
46646 		if( pComp->nByte > 0 ){
46647 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46648 			jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
46649 		}
46650 		/* Reset the string cursor */
46651 		jx9_value_reset_string_cursor(pValue);
46652 		pComp = &sURI.sFragment;
46653 		if( pComp->nByte > 0 ){
46654 			jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
46655 			jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
46656 		}
46657 		/* Return the created array */
46658 		jx9_result_value(pCtx, pArray);
46659 		/* NOTE:
46660 		 * Don't worry about freeing 'pValue', everything will be released
46661 		 * automatically as soon we return from this function.
46662 		 */
46663 	}
46664 	/* All done */
46665 	return JX9_OK;
46666 }
46667 /*
46668  * Section:
46669  *   Array related routines.
46670  * Authors:
46671  *    Symisc Systems, devel@symisc.net.
46672  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
46673  * Status:
46674  *    Stable.
46675  * Note 2012-5-21 01:04:15:
46676  *  Array related functions that need access to the underlying
46677  *  virtual machine are implemented here rather than 'hashmap.c'
46678  */
46679 /*
46680  * The [extract()] function store it's state information in an instance
46681  * of the following structure.
46682  */
46683 typedef struct extract_aux_data extract_aux_data;
46684 struct extract_aux_data
46685 {
46686 	jx9_vm *pVm;          /* VM that own this instance */
46687 	int iCount;           /* Number of variables successfully imported  */
46688 	const char *zPrefix;  /* Prefix name */
46689 	int Prefixlen;        /* Prefix  length */
46690 	int iFlags;           /* Control flags */
46691 	char zWorker[1024];   /* Working buffer */
46692 };
46693 /* Forward declaration */
46694 static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData);
46695 /*
46696  * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]])
46697  *   Import variables into the current symbol table from an array.
46698  * Parameters
46699  * $var_array
46700  *  An associative array. This function treats keys as variable names and values
46701  *  as variable values. For each key/value pair it will create a variable in the current symbol
46702  *  table, subject to extract_type and prefix parameters.
46703  *  You must use an associative array; a numerically indexed array will not produce results
46704  *  unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID.
46705  * $extract_type
46706  *  The way invalid/numeric keys and collisions are treated is determined by the extract_type.
46707  *  It can be one of the following values:
46708  *   EXTR_OVERWRITE
46709  *       If there is a collision, overwrite the existing variable.
46710  *   EXTR_SKIP
46711  *       If there is a collision, don't overwrite the existing variable.
46712  *   EXTR_PREFIX_SAME
46713  *       If there is a collision, prefix the variable name with prefix.
46714  *   EXTR_PREFIX_ALL
46715  *       Prefix all variable names with prefix.
46716  *   EXTR_PREFIX_INVALID
46717  *       Only prefix invalid/numeric variable names with prefix.
46718  *   EXTR_IF_EXISTS
46719  *       Only overwrite the variable if it already exists in the current symbol table
46720  *       otherwise do nothing.
46721  *       This is useful for defining a list of valid variables and then extracting only those
46722  *       variables you have defined out of $_REQUEST, for example.
46723  *   EXTR_PREFIX_IF_EXISTS
46724  *       Only create prefixed variable names if the non-prefixed version of the same variable exists in
46725  *      the current symbol table.
46726  * $prefix
46727  *  Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL
46728  *  EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name
46729  *  it is not imported into the symbol table. Prefixes are automatically separated from the array key by an
46730  *  underscore character.
46731  * Return
46732  *   Returns the number of variables successfully imported into the symbol table.
46733  */
vm_builtin_extract(jx9_context * pCtx,int nArg,jx9_value ** apArg)46734 static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg)
46735 {
46736 	extract_aux_data sAux;
46737 	jx9_hashmap *pMap;
46738 	if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
46739 		/* Missing/Invalid arguments, return 0 */
46740 		jx9_result_int(pCtx, 0);
46741 		return JX9_OK;
46742 	}
46743 	/* Point to the target hashmap */
46744 	pMap = (jx9_hashmap *)apArg[0]->x.pOther;
46745 	if( pMap->nEntry < 1 ){
46746 		/* Empty map, return  0 */
46747 		jx9_result_int(pCtx, 0);
46748 		return JX9_OK;
46749 	}
46750 	/* Prepare the aux data */
46751 	SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
46752 	if( nArg > 1 ){
46753 		sAux.iFlags = jx9_value_to_int(apArg[1]);
46754 		if( nArg > 2 ){
46755 			sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
46756 		}
46757 	}
46758 	sAux.pVm = pCtx->pVm;
46759 	/* Invoke the worker callback */
46760 	jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
46761 	/* Number of variables successfully imported */
46762 	jx9_result_int(pCtx, sAux.iCount);
46763 	return JX9_OK;
46764 }
46765 /*
46766  * Worker callback for the [extract()] function defined
46767  * below.
46768  */
VmExtractCallback(jx9_value * pKey,jx9_value * pValue,void * pUserData)46769 static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
46770 {
46771 	extract_aux_data *pAux = (extract_aux_data *)pUserData;
46772 	int iFlags = pAux->iFlags;
46773 	jx9_vm *pVm = pAux->pVm;
46774 	jx9_value *pObj;
46775 	SyString sVar;
46776 	if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){
46777 		iFlags |= 0x08; /*EXTR_PREFIX_ALL*/
46778 	}
46779 	/* Perform a string cast */
46780 	jx9MemObjToString(pKey);
46781 	if( SyBlobLength(&pKey->sBlob) < 1 ){
46782 		/* Unavailable variable name */
46783 		return SXRET_OK;
46784 	}
46785 	sVar.nByte = 0; /* cc warning */
46786 	if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){
46787 		sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s",
46788 			pAux->Prefixlen, pAux->zPrefix,
46789 			SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
46790 			);
46791 	}else{
46792 		sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker,
46793 			SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker)));
46794 	}
46795 	sVar.zString = pAux->zWorker;
46796 	/* Try to extract the variable */
46797 	pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE);
46798 	if( pObj ){
46799 		/* Collision */
46800 		if( iFlags & 0x02 /* EXTR_SKIP */ ){
46801 			return SXRET_OK;
46802 		}
46803 		if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){
46804 			if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){
46805 				/* Already prefixed */
46806 				return SXRET_OK;
46807 			}
46808 			sVar.nByte = SyBufferFormat(
46809 				pAux->zWorker, sizeof(pAux->zWorker),
46810 				"%.*s_%.*s",
46811 				pAux->Prefixlen, pAux->zPrefix,
46812 				SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
46813 				);
46814 			pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
46815 		}
46816 	}else{
46817 		/* Create the variable */
46818 		pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
46819 	}
46820 	if( pObj ){
46821 		/* Overwrite the old value */
46822 		jx9MemObjStore(pValue, pObj);
46823 		/* Increment counter */
46824 		pAux->iCount++;
46825 	}
46826 	return SXRET_OK;
46827 }
46828 /*
46829  * Compile and evaluate a JX9 chunk at run-time.
46830  * Refer to the include language construct implementation for more
46831  * information.
46832  */
VmEvalChunk(jx9_vm * pVm,jx9_context * pCtx,SyString * pChunk,int iFlags,int bTrueReturn)46833 static sxi32 VmEvalChunk(
46834 	jx9_vm *pVm,        /* Underlying Virtual Machine */
46835 	jx9_context *pCtx,  /* Call Context */
46836 	SyString *pChunk,   /* JX9 chunk to evaluate */
46837 	int iFlags,         /* Compile flag */
46838 	int bTrueReturn     /* TRUE to return execution result */
46839 	)
46840 {
46841 	SySet *pByteCode, aByteCode;
46842 	ProcConsumer xErr = 0;
46843 	void *pErrData = 0;
46844 	/* Initialize bytecode container */
46845 	SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr));
46846 	SySetAlloc(&aByteCode, 0x20);
46847 	/* Reset the code generator */
46848 	if( bTrueReturn ){
46849 		/* Included file, log compile-time errors */
46850 		xErr = pVm->pEngine->xConf.xErr;
46851 		pErrData = pVm->pEngine->xConf.pErrData;
46852 	}
46853 	jx9ResetCodeGenerator(pVm, xErr, pErrData);
46854 	/* Swap bytecode container */
46855 	pByteCode = pVm->pByteContainer;
46856 	pVm->pByteContainer = &aByteCode;
46857 	/* Compile the chunk */
46858 	jx9CompileScript(pVm, pChunk, iFlags);
46859 	if( pVm->sCodeGen.nErr > 0 ){
46860 		/* Compilation error, return false */
46861 		if( pCtx ){
46862 			jx9_result_bool(pCtx, 0);
46863 		}
46864 	}else{
46865 		jx9_value sResult; /* Return value */
46866 		if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
46867 			/* Out of memory */
46868 			if( pCtx ){
46869 				jx9_result_bool(pCtx, 0);
46870 			}
46871 			goto Cleanup;
46872 		}
46873 		if( bTrueReturn ){
46874 			/* Assume a boolean true return value */
46875 			jx9MemObjInitFromBool(pVm, &sResult, 1);
46876 		}else{
46877 			/* Assume a null return value */
46878 			jx9MemObjInit(pVm, &sResult);
46879 		}
46880 		/* Execute the compiled chunk */
46881 		VmLocalExec(pVm, &aByteCode, &sResult);
46882 		if( pCtx ){
46883 			/* Set the execution result */
46884 			jx9_result_value(pCtx, &sResult);
46885 		}
46886 		jx9MemObjRelease(&sResult);
46887 	}
46888 Cleanup:
46889 	/* Cleanup the mess left behind */
46890 	pVm->pByteContainer = pByteCode;
46891 	SySetRelease(&aByteCode);
46892 	return SXRET_OK;
46893 }
46894 /*
46895  * Check if a file path is already included.
46896  */
VmIsIncludedFile(jx9_vm * pVm,SyString * pFile)46897 static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile)
46898 {
46899 	SyString *aEntries;
46900 	sxu32 n;
46901 	aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded);
46902 	/* Perform a linear search */
46903 	for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){
46904 		if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){
46905 			/* Already included */
46906 			return TRUE;
46907 		}
46908 	}
46909 	return FALSE;
46910 }
46911 /*
46912  * Push a file path in the appropriate VM container.
46913  */
jx9VmPushFilePath(jx9_vm * pVm,const char * zPath,int nLen,sxu8 bMain,sxi32 * pNew)46914 JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew)
46915 {
46916 	SyString sPath;
46917 	char *zDup;
46918 #ifdef __WINNT__
46919 	char *zCur;
46920 #endif
46921 	sxi32 rc;
46922 	if( nLen < 0 ){
46923 		nLen = SyStrlen(zPath);
46924 	}
46925 	/* Duplicate the file path first */
46926 	zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen);
46927 	if( zDup == 0 ){
46928 		return SXERR_MEM;
46929 	}
46930 #ifdef __WINNT__
46931 	/* Normalize path on windows
46932 	 * Example:
46933 	 *    Path/To/File.jx9
46934 	 * becomes
46935 	 *   path\to\file.jx9
46936 	 */
46937 	zCur = zDup;
46938 	while( zCur[0] != 0 ){
46939 		if( zCur[0] == '/' ){
46940 			zCur[0] = '\\';
46941 		}else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){
46942 			int c = SyToLower(zCur[0]);
46943 			zCur[0] = (char)c; /* MSVC stupidity */
46944 		}
46945 		zCur++;
46946 	}
46947 #endif
46948 	/* Install the file path */
46949 	SyStringInitFromBuf(&sPath, zDup, nLen);
46950 	if( !bMain ){
46951 		if( VmIsIncludedFile(&(*pVm), &sPath) ){
46952 			/* Already included */
46953 			*pNew = 0;
46954 		}else{
46955 			/* Insert in the corresponding container */
46956 			rc = SySetPut(&pVm->aIncluded, (const void *)&sPath);
46957 			if( rc != SXRET_OK ){
46958 				SyMemBackendFree(&pVm->sAllocator, zDup);
46959 				return rc;
46960 			}
46961 			*pNew = 1;
46962 		}
46963 	}
46964 	SySetPut(&pVm->aFiles, (const void *)&sPath);
46965 	return SXRET_OK;
46966 }
46967 /*
46968  * Compile and Execute a JX9 script at run-time.
46969  * SXRET_OK is returned on sucessful evaluation.Any other return values
46970  * indicates failure.
46971  * Note that the JX9 script to evaluate can be a local or remote file.In
46972  * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
46973  * operations.
46974  * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
46975  * this function is a no-op.
46976  * Refer to the implementation of the include(), import() language
46977  * constructs for more information.
46978  */
VmExecIncludedFile(jx9_context * pCtx,SyString * pPath,int IncludeOnce)46979 static sxi32 VmExecIncludedFile(
46980 	 jx9_context *pCtx, /* Call Context */
46981 	 SyString *pPath,   /* Script path or URL*/
46982 	 int IncludeOnce    /* TRUE if called from import() or require_once() */
46983 	 )
46984 {
46985 	sxi32 rc;
46986 #ifndef JX9_DISABLE_BUILTIN_FUNC
46987 	const jx9_io_stream *pStream;
46988 	SyBlob sContents;
46989 	void *pHandle;
46990 	jx9_vm *pVm;
46991 	int isNew;
46992 	/* Initialize fields */
46993 	pVm = pCtx->pVm;
46994 	SyBlobInit(&sContents, &pVm->sAllocator);
46995 	isNew = 0;
46996 	/* Extract the associated stream */
46997 	pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
46998 	/*
46999 	 * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"]
47000 	 * in a read-only mode.
47001 	 */
47002 	pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew);
47003 	if( pHandle == 0 ){
47004 		return SXERR_IO;
47005 	}
47006 	rc = SXRET_OK; /* Stupid cc warning */
47007 	if( IncludeOnce && !isNew ){
47008 		/* Already included */
47009 		rc = SXERR_EXISTS;
47010 	}else{
47011 		/* Read the whole file contents */
47012 		rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents);
47013 		if( rc == SXRET_OK ){
47014 			SyString sScript;
47015 			/* Compile and execute the script */
47016 			SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
47017 			VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE);
47018 		}
47019 	}
47020 	/* Pop from the set of included file */
47021 	(void)SySetPop(&pVm->aFiles);
47022 	/* Close the handle */
47023 	jx9StreamCloseHandle(pStream, pHandle);
47024 	/* Release the working buffer */
47025 	SyBlobRelease(&sContents);
47026 #else
47027 	pCtx = 0; /* cc warning */
47028 	pPath = 0;
47029 	IncludeOnce = 0;
47030 	rc = SXERR_IO;
47031 #endif /* JX9_DISABLE_BUILTIN_FUNC */
47032 	return rc;
47033 }
47034 /* * include:
47035  * According to the JX9 reference manual.
47036  *  The include() function includes and evaluates the specified file.
47037  *  Files are included based on the file path given or, if none is given
47038  *  the include_path specified.If the file isn't found in the include_path
47039  *  include() will finally check in the calling script's own directory
47040  *  and the current working directory before failing. The include()
47041  *  construct will emit a warning if it cannot find a file; this is different
47042  *  behavior from require(), which will emit a fatal error.
47043  *  If a path is defined — whether absolute (starting with a drive letter
47044  *  or \ on Windows, or / on Unix/Linux systems) or relative to the current
47045  *  directory (starting with . or ..) — the include_path will be ignored altogether.
47046  *  For example, if a filename begins with ../, the parser will look in the parent
47047  *  directory to find the requested file.
47048  *  When a file is included, the code it contains inherits the variable scope
47049  *  of the line on which the include occurs. Any variables available at that line
47050  *  in the calling file will be available within the called file, from that point forward.
47051  *  However, all functions and objectes defined in the included file have the global scope.
47052  */
vm_builtin_include(jx9_context * pCtx,int nArg,jx9_value ** apArg)47053 static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
47054 {
47055 	SyString sFile;
47056 	sxi32 rc;
47057 	if( nArg < 1 ){
47058 		/* Nothing to evaluate, return NULL */
47059 		jx9_result_null(pCtx);
47060 		return SXRET_OK;
47061 	}
47062 	/* File to include */
47063 	sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
47064 	if( sFile.nByte < 1 ){
47065 		/* Empty string, return NULL */
47066 		jx9_result_null(pCtx);
47067 		return SXRET_OK;
47068 	}
47069 	/* Open, compile and execute the desired script */
47070 	rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE);
47071 	if( rc != SXRET_OK ){
47072 		/* Emit a warning and return false */
47073 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
47074 		jx9_result_bool(pCtx, 0);
47075 	}
47076 	return SXRET_OK;
47077 }
47078 /*
47079  * import:
47080  *  According to the JX9 reference manual.
47081  *   The import() statement includes and evaluates the specified file during
47082  *   the execution of the script. This is a behavior similar to the include()
47083  *   statement, with the only difference being that if the code from a file has already
47084  *   been included, it will not be included again. As the name suggests, it will be included
47085  *   just once.
47086  */
vm_builtin_import(jx9_context * pCtx,int nArg,jx9_value ** apArg)47087 static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
47088 {
47089 	SyString sFile;
47090 	sxi32 rc;
47091 	if( nArg < 1 ){
47092 		/* Nothing to evaluate, return NULL */
47093 		jx9_result_null(pCtx);
47094 		return SXRET_OK;
47095 	}
47096 	/* File to include */
47097 	sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
47098 	if( sFile.nByte < 1 ){
47099 		/* Empty string, return NULL */
47100 		jx9_result_null(pCtx);
47101 		return SXRET_OK;
47102 	}
47103 	/* Open, compile and execute the desired script */
47104 	rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
47105 	if( rc == SXERR_EXISTS ){
47106 		/* File already included, return TRUE */
47107 		jx9_result_bool(pCtx, 1);
47108 		return SXRET_OK;
47109 	}
47110 	if( rc != SXRET_OK ){
47111 		/* Emit a warning and return false */
47112 		jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
47113 		jx9_result_bool(pCtx, 0);
47114  	}
47115 	return SXRET_OK;
47116 }
47117 /*
47118  * Section:
47119  *  Command line arguments processing.
47120  * Authors:
47121  *    Symisc Systems, devel@symisc.net.
47122  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
47123  * Status:
47124  *    Stable.
47125  */
47126 /*
47127  * Check if a short option argument [i.e: -c] is available in the command
47128  * line string. Return a pointer to the start of the stream on success.
47129  * NULL otherwise.
47130  */
VmFindShortOpt(int c,const char * zIn,const char * zEnd)47131 static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd)
47132 {
47133 	while( zIn < zEnd ){
47134 		if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){
47135 			/* Got one */
47136 			return &zIn[1];
47137 		}
47138 		/* Advance the cursor */
47139 		zIn++;
47140 	}
47141 	/* No such option */
47142 	return 0;
47143 }
47144 /*
47145  * Check if a long option argument [i.e: --opt] is available in the command
47146  * line string. Return a pointer to the start of the stream on success.
47147  * NULL otherwise.
47148  */
VmFindLongOpt(const char * zLong,int nByte,const char * zIn,const char * zEnd)47149 static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd)
47150 {
47151 	const char *zOpt;
47152 	while( zIn < zEnd ){
47153 		if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){
47154 			zIn += 2;
47155 			zOpt = zIn;
47156 			while( zIn < zEnd && !SyisSpace(zIn[0]) ){
47157 				if( zIn[0] == '=' /* --opt=val */){
47158 					break;
47159 				}
47160 				zIn++;
47161 			}
47162 			/* Test */
47163 			if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){
47164 				/* Got one, return it's value */
47165 				return zIn;
47166 			}
47167 
47168 		}else{
47169 			zIn++;
47170 		}
47171 	}
47172 	/* No such option */
47173 	return 0;
47174 }
47175 /*
47176  * Long option [i.e: --opt] arguments private data structure.
47177  */
47178 struct getopt_long_opt
47179 {
47180 	const char *zArgIn, *zArgEnd; /* Command line arguments */
47181 	jx9_value *pWorker;  /* Worker variable*/
47182 	jx9_value *pArray;   /* getopt() return value */
47183 	jx9_context *pCtx;   /* Call Context */
47184 };
47185 /* Forward declaration */
47186 static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
47187 /*
47188  * Extract short or long argument option values.
47189  */
VmExtractOptArgValue(jx9_value * pArray,jx9_value * pWorker,const char * zArg,const char * zArgEnd,int need_val,jx9_context * pCtx,const char * zName)47190 static void VmExtractOptArgValue(
47191 	jx9_value *pArray,  /* getopt() return value */
47192 	jx9_value *pWorker, /* Worker variable */
47193 	const char *zArg,   /* Argument stream */
47194 	const char *zArgEnd, /* End of the argument stream  */
47195 	int need_val,       /* TRUE to fetch option argument */
47196 	jx9_context *pCtx,  /* Call Context */
47197 	const char *zName   /* Option name */)
47198 {
47199 	jx9_value_bool(pWorker, 0);
47200 	if( !need_val ){
47201 		/*
47202 		 * Option does not need arguments.
47203 		 * Insert the option name and a boolean FALSE.
47204 		 */
47205 		jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47206 	}else{
47207 		const char *zCur;
47208 		/* Extract option argument */
47209 		zArg++;
47210 		if( zArg < zArgEnd && zArg[0] == '=' ){
47211 			zArg++;
47212 		}
47213 		while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47214 			zArg++;
47215 		}
47216 		if( zArg >= zArgEnd || zArg[0] == '-' ){
47217 			/*
47218 			 * Argument not found.
47219 			 * Insert the option name and a boolean FALSE.
47220 			 */
47221 			jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47222 			return;
47223 		}
47224 		/* Delimit the value */
47225 		zCur = zArg;
47226 		if( zArg[0] == '\'' || zArg[0] == '"' ){
47227 			int d = zArg[0];
47228 			/* Delimt the argument */
47229 			zArg++;
47230 			zCur = zArg;
47231 			while( zArg < zArgEnd ){
47232 				if( zArg[0] == d && zArg[-1] != '\\' ){
47233 					/* Delimiter found, exit the loop  */
47234 					break;
47235 				}
47236 				zArg++;
47237 			}
47238 			/* Save the value */
47239 			jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47240 			if( zArg < zArgEnd ){ zArg++; }
47241 		}else{
47242 			while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
47243 				zArg++;
47244 			}
47245 			/* Save the value */
47246 			jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47247 		}
47248 		/*
47249 		 * Check if we are dealing with multiple values.
47250 		 * If so, create an array to hold them, rather than a scalar variable.
47251 		 */
47252 		while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47253 			zArg++;
47254 		}
47255 		if( zArg < zArgEnd && zArg[0] != '-' ){
47256 			jx9_value *pOptArg; /* Array of option arguments */
47257 			pOptArg = jx9_context_new_array(pCtx);
47258 			if( pOptArg == 0 ){
47259 				jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
47260 			}else{
47261 				/* Insert the first value */
47262 				jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
47263 				for(;;){
47264 					if( zArg >= zArgEnd || zArg[0] == '-' ){
47265 						/* No more value */
47266 						break;
47267 					}
47268 					/* Delimit the value */
47269 					zCur = zArg;
47270 					if( zArg < zArgEnd && zArg[0] == '\\' ){
47271 						zArg++;
47272 						zCur = zArg;
47273 					}
47274 					while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
47275 						zArg++;
47276 					}
47277 					/* Reset the string cursor */
47278 					jx9_value_reset_string_cursor(pWorker);
47279 					/* Save the value */
47280 					jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
47281 					/* Insert */
47282 					jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
47283 					/* Jump trailing white spaces */
47284 					while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
47285 						zArg++;
47286 					}
47287 				}
47288 				/* Insert the option arg array */
47289 				jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
47290 				/* Safely release */
47291 				jx9_context_release_value(pCtx, pOptArg);
47292 			}
47293 		}else{
47294 			/* Single value */
47295 			jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
47296 		}
47297 	}
47298 }
47299 /*
47300  * array getopt(string $options[, array $longopts ])
47301  *   Gets options from the command line argument list.
47302  * Parameters
47303  *  $options
47304  *   Each character in this string will be used as option characters
47305  *   and matched against options passed to the script starting with
47306  *   a single hyphen (-). For example, an option string "x" recognizes
47307  *   an option -x. Only a-z, A-Z and 0-9 are allowed.
47308  *  $longopts
47309  *   An array of options. Each element in this array will be used as option
47310  *   strings and matched against options passed to the script starting with
47311  *   two hyphens (--). For example, an longopts element "opt" recognizes an
47312  *   option --opt.
47313  * Return
47314  *  This function will return an array of option / argument pairs or FALSE
47315  *  on failure.
47316  */
vm_builtin_getopt(jx9_context * pCtx,int nArg,jx9_value ** apArg)47317 static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg)
47318 {
47319 	const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
47320 	struct getopt_long_opt sLong;
47321 	jx9_value *pArray, *pWorker;
47322 	SyBlob *pArg;
47323 	int nByte;
47324 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
47325 		/* Missing/Invalid arguments, return FALSE */
47326 		jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
47327 		jx9_result_bool(pCtx, 0);
47328 		return JX9_OK;
47329 	}
47330 	/* Extract option arguments */
47331 	zIn  = jx9_value_to_string(apArg[0], &nByte);
47332 	zEnd = &zIn[nByte];
47333 	/* Point to the string representation of the $argv[] array */
47334 	pArg = &pCtx->pVm->sArgv;
47335 	/* Create a new empty array and a worker variable */
47336 	pArray = jx9_context_new_array(pCtx);
47337 	pWorker = jx9_context_new_scalar(pCtx);
47338 	if( pArray == 0 || pWorker == 0 ){
47339 		jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
47340 		jx9_result_bool(pCtx, 0);
47341 		return JX9_OK;
47342 	}
47343 	if( SyBlobLength(pArg) < 1 ){
47344 		/* Empty command line, return the empty array*/
47345 		jx9_result_value(pCtx, pArray);
47346 		/* Everything will be released automatically when we return
47347 		 * from this function.
47348 		 */
47349 		return JX9_OK;
47350 	}
47351 	zArgIn = (const char *)SyBlobData(pArg);
47352 	zArgEnd = &zArgIn[SyBlobLength(pArg)];
47353 	/* Fill the long option structure */
47354 	sLong.pArray = pArray;
47355 	sLong.pWorker = pWorker;
47356 	sLong.zArgIn =  zArgIn;
47357 	sLong.zArgEnd = zArgEnd;
47358 	sLong.pCtx = pCtx;
47359 	/* Start processing */
47360 	while( zIn < zEnd ){
47361 		int c = zIn[0];
47362 		int need_val = 0;
47363 		/* Advance the stream cursor */
47364 		zIn++;
47365 		/* Ignore non-alphanum characters */
47366 		if( !SyisAlphaNum(c) ){
47367 			continue;
47368 		}
47369 		if( zIn < zEnd && zIn[0] == ':' ){
47370 			zIn++;
47371 			need_val = 1;
47372 			if( zIn < zEnd && zIn[0] == ':' ){
47373 				zIn++;
47374 			}
47375 		}
47376 		/* Find option */
47377 		zArg = VmFindShortOpt(c, zArgIn, zArgEnd);
47378 		if( zArg == 0 ){
47379 			/* No such option */
47380 			continue;
47381 		}
47382 		/* Extract option argument value */
47383 		VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c);
47384 	}
47385 	if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
47386 		/* Process long options */
47387 		jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
47388 	}
47389 	/* Return the option array */
47390 	jx9_result_value(pCtx, pArray);
47391 	/*
47392 	 * Don't worry about freeing memory, everything will be released
47393 	 * automatically as soon we return from this foreign function.
47394 	 */
47395 	return JX9_OK;
47396 }
47397 /*
47398  * Array walker callback used for processing long options values.
47399  */
VmProcessLongOpt(jx9_value * pKey,jx9_value * pValue,void * pUserData)47400 static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData)
47401 {
47402 	struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData;
47403 	const char *zArg, *zOpt, *zEnd;
47404 	int need_value = 0;
47405 	int nByte;
47406 	/* Value must be of type string */
47407 	if( !jx9_value_is_string(pValue) ){
47408 		/* Simply ignore */
47409 		return JX9_OK;
47410 	}
47411 	zOpt = jx9_value_to_string(pValue, &nByte);
47412 	if( nByte < 1 ){
47413 		/* Empty string, ignore */
47414 		return JX9_OK;
47415 	}
47416 	zEnd = &zOpt[nByte - 1];
47417 	if( zEnd[0] == ':' ){
47418 		char *zTerm;
47419 		/* Try to extract a value */
47420 		need_value = 1;
47421 		while( zEnd >= zOpt && zEnd[0] == ':' ){
47422 			zEnd--;
47423 		}
47424 		if( zOpt >= zEnd ){
47425 			/* Empty string, ignore */
47426 			SXUNUSED(pKey);
47427 			return JX9_OK;
47428 		}
47429 		zEnd++;
47430 		zTerm = (char *)zEnd;
47431 		zTerm[0] = 0;
47432 	}else{
47433 		zEnd = &zOpt[nByte];
47434 	}
47435 	/* Find the option */
47436 	zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd);
47437 	if( zArg == 0 ){
47438 		/* No such option, return immediately */
47439 		return JX9_OK;
47440 	}
47441 	/* Try to extract a value */
47442 	VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
47443 	return JX9_OK;
47444 }
47445 /*
47446  * int utf8_encode(string $input)
47447  *  UTF-8 encoding.
47448  *  This function encodes the string data to UTF-8, and returns the encoded version.
47449  *  UTF-8 is a standard mechanism used by Unicode for encoding wide character values
47450  * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized
47451  * (meaning it is possible for a program to figure out where in the bytestream characters start)
47452  * and can be used with normal string comparison functions for sorting and such.
47453  *  Notes on UTF-8 (According to SQLite3 authors):
47454  *  Byte-0    Byte-1    Byte-2    Byte-3    Value
47455  *  0xxxxxxx                                 00000000 00000000 0xxxxxxx
47456  *  110yyyyy  10xxxxxx                       00000000 00000yyy yyxxxxxx
47457  *  1110zzzz  10yyyyyy  10xxxxxx             00000000 zzzzyyyy yyxxxxxx
47458  *  11110uuu  10uuzzzz  10yyyyyy  10xxxxxx   000uuuuu zzzzyyyy yyxxxxxx
47459  * Parameters
47460  * $input
47461  *   String to encode or NULL on failure.
47462  * Return
47463  *  An UTF-8 encoded string.
47464  */
vm_builtin_utf8_encode(jx9_context * pCtx,int nArg,jx9_value ** apArg)47465 static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47466 {
47467 	const unsigned char *zIn, *zEnd;
47468 	int nByte, c, e;
47469 	if( nArg < 1 ){
47470 		/* Missing arguments, return null */
47471 		jx9_result_null(pCtx);
47472 		return JX9_OK;
47473 	}
47474 	/* Extract the target string */
47475 	zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
47476 	if( nByte < 1 ){
47477 		/* Empty string, return null */
47478 		jx9_result_null(pCtx);
47479 		return JX9_OK;
47480 	}
47481 	zEnd = &zIn[nByte];
47482 	/* Start the encoding process */
47483 	for(;;){
47484 		if( zIn >= zEnd ){
47485 			/* End of input */
47486 			break;
47487 		}
47488 		c = zIn[0];
47489 		/* Advance the stream cursor */
47490 		zIn++;
47491 		/* Encode */
47492 		if( c<0x00080 ){
47493 			e = (c&0xFF);
47494 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47495 		}else if( c<0x00800 ){
47496 			e = 0xC0 + ((c>>6)&0x1F);
47497 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47498 			e = 0x80 + (c & 0x3F);
47499 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47500 		}else if( c<0x10000 ){
47501 			e = 0xE0 + ((c>>12)&0x0F);
47502 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47503 			e = 0x80 + ((c>>6) & 0x3F);
47504 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47505 			e = 0x80 + (c & 0x3F);
47506 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47507 		}else{
47508 			e = 0xF0 + ((c>>18) & 0x07);
47509 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47510 			e = 0x80 + ((c>>12) & 0x3F);
47511 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47512 			e = 0x80 + ((c>>6) & 0x3F);
47513 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47514 			e = 0x80 + (c & 0x3F);
47515 			jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
47516 		}
47517 	}
47518 	/* All done */
47519 	return JX9_OK;
47520 }
47521 /*
47522  * UTF-8 decoding routine extracted from the sqlite3 source tree.
47523  * Original author: D. Richard Hipp (http://www.sqlite.org)
47524  * Status: Public Domain
47525  */
47526 /*
47527 ** This lookup table is used to help decode the first byte of
47528 ** a multi-byte UTF8 character.
47529 */
47530 static const unsigned char UtfTrans1[] = {
47531   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47532   0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
47533   0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
47534   0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
47535   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47536   0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
47537   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47538   0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
47539 };
47540 /*
47541 ** Translate a single UTF-8 character.  Return the unicode value.
47542 **
47543 ** During translation, assume that the byte that zTerm points
47544 ** is a 0x00.
47545 **
47546 ** Write a pointer to the next unread byte back into *pzNext.
47547 **
47548 ** Notes On Invalid UTF-8:
47549 **
47550 **  *  This routine never allows a 7-bit character (0x00 through 0x7f) to
47551 **     be encoded as a multi-byte character.  Any multi-byte character that
47552 **     attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
47553 **
47554 **  *  This routine never allows a UTF16 surrogate value to be encoded.
47555 **     If a multi-byte character attempts to encode a value between
47556 **     0xd800 and 0xe000 then it is rendered as 0xfffd.
47557 **
47558 **  *  Bytes in the range of 0x80 through 0xbf which occur as the first
47559 **     byte of a character are interpreted as single-byte characters
47560 **     and rendered as themselves even though they are technically
47561 **     invalid characters.
47562 **
47563 **  *  This routine accepts an infinite number of different UTF8 encodings
47564 **     for unicode values 0x80 and greater.  It do not change over-length
47565 **     encodings to 0xfffd as some systems recommend.
47566 */
47567 #define READ_UTF8(zIn, zTerm, c)                           \
47568   c = *(zIn++);                                            \
47569   if( c>=0xc0 ){                                           \
47570     c = UtfTrans1[c-0xc0];                                 \
47571     while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){            \
47572       c = (c<<6) + (0x3f & *(zIn++));                      \
47573     }                                                      \
47574     if( c<0x80                                             \
47575         || (c&0xFFFFF800)==0xD800                          \
47576         || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }        \
47577   }
jx9Utf8Read(const unsigned char * z,const unsigned char * zTerm,const unsigned char ** pzNext)47578 JX9_PRIVATE int jx9Utf8Read(
47579   const unsigned char *z,         /* First byte of UTF-8 character */
47580   const unsigned char *zTerm,     /* Pretend this byte is 0x00 */
47581   const unsigned char **pzNext    /* Write first byte past UTF-8 char here */
47582 ){
47583   int c;
47584   READ_UTF8(z, zTerm, c);
47585   *pzNext = z;
47586   return c;
47587 }
47588 /*
47589  * string utf8_decode(string $data)
47590  *  This function decodes data, assumed to be UTF-8 encoded, to unicode.
47591  * Parameters
47592  * data
47593  *  An UTF-8 encoded string.
47594  * Return
47595  *  Unicode decoded string or NULL on failure.
47596  */
vm_builtin_utf8_decode(jx9_context * pCtx,int nArg,jx9_value ** apArg)47597 static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47598 {
47599 	const unsigned char *zIn, *zEnd;
47600 	int nByte, c;
47601 	if( nArg < 1 ){
47602 		/* Missing arguments, return null */
47603 		jx9_result_null(pCtx);
47604 		return JX9_OK;
47605 	}
47606 	/* Extract the target string */
47607 	zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
47608 	if( nByte < 1 ){
47609 		/* Empty string, return null */
47610 		jx9_result_null(pCtx);
47611 		return JX9_OK;
47612 	}
47613 	zEnd = &zIn[nByte];
47614 	/* Start the decoding process */
47615 	while( zIn < zEnd ){
47616 		c = jx9Utf8Read(zIn, zEnd, &zIn);
47617 		if( c == 0x0 ){
47618 			break;
47619 		}
47620 		jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
47621 	}
47622 	return JX9_OK;
47623 }
47624 /*
47625  * string json_encode(mixed $value)
47626  *  Returns a string containing the JSON representation of value.
47627  * Parameters
47628  *  $value
47629  *  The value being encoded. Can be any type except a resource.
47630  * Return
47631  *  Returns a JSON encoded string on success. FALSE otherwise
47632  */
vm_builtin_json_encode(jx9_context * pCtx,int nArg,jx9_value ** apArg)47633 static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
47634 {
47635 	SyBlob sBlob;
47636 	if( nArg < 1 ){
47637 		/* Missing arguments, return FALSE */
47638 		jx9_result_bool(pCtx, 0);
47639 		return JX9_OK;
47640 	}
47641 	/* Init the working buffer */
47642 	SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
47643 	/* Perform the encoding operation */
47644 	jx9JsonSerialize(apArg[0],&sBlob);
47645 	/* Return the serialized value */
47646 	jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
47647 	/* Cleanup */
47648 	SyBlobRelease(&sBlob);
47649 	/* All done */
47650 	return JX9_OK;
47651 }
47652 /*
47653  * mixed json_decode(string $json)
47654  *  Takes a JSON encoded string and converts it into a JX9 variable.
47655  * Parameters
47656  *  $json
47657  *    The json string being decoded.
47658  * Return
47659  *  The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive)
47660  *  are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
47661  *  or if the encoded data is deeper than the recursion limit.
47662  */
vm_builtin_json_decode(jx9_context * pCtx,int nArg,jx9_value ** apArg)47663 static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
47664 {
47665 	const char *zJSON;
47666 	int nByte;
47667 	if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
47668 		/* Missing/Invalid arguments, return NULL */
47669 		jx9_result_null(pCtx);
47670 		return JX9_OK;
47671 	}
47672 	/* Extract the JSON string */
47673 	zJSON = jx9_value_to_string(apArg[0], &nByte);
47674 	if( nByte < 1 ){
47675 		/* Empty string, return NULL */
47676 		jx9_result_null(pCtx);
47677 		return JX9_OK;
47678 	}
47679 	/* Decode the raw JSON */
47680 	jx9JsonDecode(pCtx,zJSON,nByte);
47681 	return JX9_OK;
47682 }
47683 /* Table of built-in VM functions. */
47684 static const jx9_builtin_func aVmFunc[] = {
47685 	     /* JSON Encoding/Decoding */
47686 	{ "json_encode",     vm_builtin_json_encode   },
47687 	{ "json_decode",     vm_builtin_json_decode   },
47688 	     /* Functions calls */
47689 	{ "func_num_args"  , vm_builtin_func_num_args },
47690 	{ "func_get_arg"   , vm_builtin_func_get_arg  },
47691 	{ "func_get_args"  , vm_builtin_func_get_args },
47692 	{ "function_exists", vm_builtin_func_exists   },
47693 	{ "is_callable"    , vm_builtin_is_callable   },
47694 	{ "get_defined_functions", vm_builtin_get_defined_func },
47695 	    /* Constants management */
47696 	{ "defined",  vm_builtin_defined              },
47697 	{ "get_defined_constants", vm_builtin_get_defined_constants },
47698 	   /* Random numbers/strings generators */
47699 	{ "rand",          vm_builtin_rand            },
47700 	{ "rand_str",      vm_builtin_rand_str        },
47701 	{ "getrandmax",    vm_builtin_getrandmax      },
47702 	   /* Language constructs functions */
47703 	{ "print", vm_builtin_print                   },
47704 	{ "exit",  vm_builtin_exit                    },
47705 	{ "die",   vm_builtin_exit                    },
47706 	  /* Variable handling functions */
47707 	{ "gettype",   vm_builtin_gettype              },
47708 	{ "get_resource_type", vm_builtin_get_resource_type},
47709 	 /* Variable dumping */
47710 	{ "dump",     vm_builtin_dump                 },
47711 	  /* Release info */
47712 	{"jx9_version",       vm_builtin_jx9_version  },
47713 	{"jx9_credits",       vm_builtin_jx9_version  },
47714 	{"jx9_info",          vm_builtin_jx9_version  },
47715 	{"jx9_copyright",     vm_builtin_jx9_version  },
47716 	  /* hashmap */
47717 	{"extract",          vm_builtin_extract       },
47718 	  /* URL related function */
47719 	{"parse_url",        vm_builtin_parse_url     },
47720 	   /* UTF-8 encoding/decoding */
47721 	{"utf8_encode",    vm_builtin_utf8_encode},
47722 	{"utf8_decode",    vm_builtin_utf8_decode},
47723 	   /* Command line processing */
47724 	{"getopt",         vm_builtin_getopt     },
47725 	   /* Files/URI inclusion facility */
47726 	{ "include",      vm_builtin_include          },
47727 	{ "import", vm_builtin_import     }
47728 };
47729 /*
47730  * Register the built-in VM functions defined above.
47731  */
VmRegisterSpecialFunction(jx9_vm * pVm)47732 static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm)
47733 {
47734 	sxi32 rc;
47735 	sxu32 n;
47736 	for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){
47737 		/* Note that these special functions have access
47738 		 * to the underlying virtual machine as their
47739 		 * private data.
47740 		 */
47741 		rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
47742 		if( rc != SXRET_OK ){
47743 			return rc;
47744 		}
47745 	}
47746 	return SXRET_OK;
47747 }
47748 #ifndef JX9_DISABLE_BUILTIN_FUNC
47749 /*
47750  * Extract the IO stream device associated with a given scheme.
47751  * Return a pointer to an instance of jx9_io_stream when the scheme
47752  * have an associated IO stream registered with it. NULL otherwise.
47753  * If no scheme:// is avalilable then the file:// scheme is assumed.
47754  * For more information on how to register IO stream devices, please
47755  * refer to the official documentation.
47756  */
jx9VmGetStreamDevice(jx9_vm * pVm,const char ** pzDevice,int nByte)47757 JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
47758 	jx9_vm *pVm,           /* Target VM */
47759 	const char **pzDevice, /* Full path, URI, ... */
47760 	int nByte              /* *pzDevice length*/
47761 	)
47762 {
47763 	const char *zIn, *zEnd, *zCur, *zNext;
47764 	jx9_io_stream **apStream, *pStream;
47765 	SyString sDev, sCur;
47766 	sxu32 n, nEntry;
47767 	int rc;
47768 	/* Check if a scheme [i.e: file://, http://, zip://...] is available */
47769 	zNext = zCur = zIn = *pzDevice;
47770 	zEnd = &zIn[nByte];
47771 	while( zIn < zEnd ){
47772 		if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){
47773 			/* Got one */
47774 			zNext = &zIn[sizeof("://")-1];
47775 			break;
47776 		}
47777 		/* Advance the cursor */
47778 		zIn++;
47779 	}
47780 	if( zIn >= zEnd ){
47781 		/* No such scheme, return the default stream */
47782 		return pVm->pDefStream;
47783 	}
47784 	SyStringInitFromBuf(&sDev, zCur, zIn-zCur);
47785 	/* Remove leading and trailing white spaces */
47786 	SyStringFullTrim(&sDev);
47787 	/* Perform a linear lookup on the installed stream devices */
47788 	apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream);
47789 	nEntry = SySetUsed(&pVm->aIOstream);
47790 	for( n = 0 ; n < nEntry ; n++ ){
47791 		pStream = apStream[n];
47792 		SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName));
47793 		/* Perfrom a case-insensitive comparison */
47794 		rc = SyStringCmp(&sDev, &sCur, SyStrnicmp);
47795 		if( rc == 0 ){
47796 			/* Stream device found */
47797 			*pzDevice = zNext;
47798 			return pStream;
47799 		}
47800 	}
47801 	/* No such stream, return NULL */
47802 	return 0;
47803 }
47804 #endif /* JX9_DISABLE_BUILTIN_FUNC */
47805 /*
47806  * Section:
47807  *    HTTP/URI related routines.
47808  * Authors:
47809  *    Symisc Systems, devel@symisc.net.
47810  *    Copyright (C) Symisc Systems, http://jx9.symisc.net
47811  * Status:
47812  *    Stable.
47813  */
47814  /*
47815   * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...].
47816   * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document]
47817   * This almost, but not quite, RFC1738 URI syntax.
47818   * This routine is not a validator, it does not check for validity
47819   * nor decode URI parts, the only thing this routine does is splitting
47820   * the input to its fields.
47821   * Upper layer are responsible of decoding and validating URI parts.
47822   * On success, this function populate the "SyhttpUri" structure passed
47823   * as the first argument. Otherwise SXERR_* is returned when a malformed
47824   * input is encountered.
47825   */
VmHttpSplitURI(SyhttpUri * pOut,const char * zUri,sxu32 nLen)47826  static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen)
47827  {
47828 	 const char *zEnd = &zUri[nLen];
47829 	 sxu8 bHostOnly = FALSE;
47830 	 sxu8 bIPv6 = FALSE	;
47831 	 const char *zCur;
47832 	 SyString *pComp;
47833 	 sxu32 nPos = 0;
47834 	 sxi32 rc;
47835 	 /* Zero the structure first */
47836 	 SyZero(pOut, sizeof(SyhttpUri));
47837 	 /* Remove leading and trailing white spaces  */
47838 	 SyStringInitFromBuf(&pOut->sRaw, zUri, nLen);
47839 	 SyStringFullTrim(&pOut->sRaw);
47840 	 /* Find the first '/' separator */
47841 	 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
47842 	 if( rc != SXRET_OK ){
47843 		 /* Assume a host name only */
47844 		 zCur = zEnd;
47845 		 bHostOnly = TRUE;
47846 		 goto ProcessHost;
47847 	 }
47848 	 zCur = &zUri[nPos];
47849 	 if( zUri != zCur && zCur[-1] == ':' ){
47850 		 /* Extract a scheme:
47851 		  * Not that we can get an invalid scheme here.
47852 		  * Fortunately the caller can discard any URI by comparing this scheme with its
47853 		  * registered schemes and will report the error as soon as his comparison function
47854 		  * fail.
47855 		  */
47856 	 	pComp = &pOut->sScheme;
47857 		SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1));
47858 		SyStringLeftTrim(pComp);
47859 	 }
47860 	 if( zCur[1] != '/' ){
47861 		 if( zCur == zUri || zCur[-1] == ':' ){
47862 		  /* No authority */
47863 		  goto PathSplit;
47864 		}
47865 		 /* There is something here , we will assume its an authority
47866 		  * and someone has forgot the two prefix slashes "//",
47867 		  * sooner or later we will detect if we are dealing with a malicious
47868 		  * user or not, but now assume we are dealing with an authority
47869 		  * and let the caller handle all the validation process.
47870 		  */
47871 		 goto ProcessHost;
47872 	 }
47873 	 zUri = &zCur[2];
47874 	 zCur = zEnd;
47875 	 rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
47876 	 if( rc == SXRET_OK ){
47877 		 zCur = &zUri[nPos];
47878 	 }
47879  ProcessHost:
47880 	 /* Extract user information if present */
47881 	 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos);
47882 	 if( rc == SXRET_OK ){
47883 		 if( nPos > 0 ){
47884 			 sxu32 nPassOfft; /* Password offset */
47885 			 pComp = &pOut->sUser;
47886 			 SyStringInitFromBuf(pComp, zUri, nPos);
47887 			 /* Extract the password if available */
47888 			 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft);
47889 			 if( rc == SXRET_OK && nPassOfft < nPos){
47890 				 pComp->nByte = nPassOfft;
47891 				 pComp = &pOut->sPass;
47892 				 pComp->zString = &zUri[nPassOfft+sizeof(char)];
47893 				 pComp->nByte = nPos - nPassOfft - 1;
47894 			 }
47895 			 /* Update the cursor */
47896 			 zUri = &zUri[nPos+1];
47897 		 }else{
47898 			 zUri++;
47899 		 }
47900 	 }
47901 	 pComp = &pOut->sHost;
47902 	 while( zUri < zCur && SyisSpace(zUri[0])){
47903 		 zUri++;
47904 	 }
47905 	 SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri));
47906 	 if( pComp->zString[0] == '[' ){
47907 		 /* An IPv6 Address: Make a simple naive test
47908 		  */
47909 		 zUri++; pComp->zString++; pComp->nByte = 0;
47910 		 while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){
47911 			 zUri++; pComp->nByte++;
47912 		 }
47913 		 if( zUri[0] != ']' ){
47914 			 return SXERR_CORRUPT; /* Malformed IPv6 address */
47915 		 }
47916 		 zUri++;
47917 		 bIPv6 = TRUE;
47918 	 }
47919 	 /* Extract a port number if available */
47920 	 rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos);
47921 	 if( rc == SXRET_OK ){
47922 		 if( bIPv6 == FALSE ){
47923 			 pComp->nByte = (sxu32)(&zUri[nPos] - zUri);
47924 		 }
47925 		 pComp = &pOut->sPort;
47926 		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1]));
47927 	 }
47928 	 if( bHostOnly == TRUE ){
47929 		 return SXRET_OK;
47930 	 }
47931 PathSplit:
47932 	 zUri = zCur;
47933 	 pComp = &pOut->sPath;
47934 	 SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri));
47935 	 if( pComp->nByte == 0 ){
47936 		 return SXRET_OK; /* Empty path */
47937 	 }
47938 	 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){
47939 		 pComp->nByte = nPos; /* Update path length */
47940 		 pComp = &pOut->sQuery;
47941 		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]));
47942 	 }
47943 	 if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){
47944 		 /* Update path or query length */
47945 		 if( pComp == &pOut->sPath ){
47946 			 pComp->nByte = nPos;
47947 		 }else{
47948 			 if( &zUri[nPos] < (char *)SyStringData(pComp) ){
47949 				 /* Malformed syntax : Query must be present before fragment */
47950 				 return SXERR_SYNTAX;
47951 			 }
47952 			 pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]);
47953 		 }
47954 		 pComp = &pOut->sFragment;
47955 		 SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]))
47956 	 }
47957 	 return SXRET_OK;
47958  }
47959  /*
47960  * Extract a single line from a raw HTTP request.
47961  * Return SXRET_OK on success, SXERR_EOF when end of input
47962  * and SXERR_MORE when more input is needed.
47963  */
VmGetNextLine(SyString * pCursor,SyString * pCurrent)47964 static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent)
47965 {
47966   	const char *zIn;
47967   	sxu32 nPos;
47968 	/* Jump leading white spaces */
47969 	SyStringLeftTrim(pCursor);
47970 	if( pCursor->nByte < 1 ){
47971 		SyStringInitFromBuf(pCurrent, 0, 0);
47972 		return SXERR_EOF; /* End of input */
47973 	}
47974 	zIn = SyStringData(pCursor);
47975 	if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){
47976 		/* Line not found, tell the caller to read more input from source */
47977 		SyStringDupPtr(pCurrent, pCursor);
47978 		return SXERR_MORE;
47979 	}
47980   	pCurrent->zString = zIn;
47981   	pCurrent->nByte	= nPos;
47982   	/* advance the cursor so we can call this routine again */
47983   	pCursor->zString = &zIn[nPos];
47984   	pCursor->nByte -= nPos;
47985   	return SXRET_OK;
47986  }
47987  /*
47988   * Split a single MIME header into a name value pair.
47989   * This function return SXRET_OK, SXERR_CONTINUE on success.
47990   * Otherwise SXERR_NEXT is returned when a malformed header
47991   * is encountered.
47992   * Note: This function handle also mult-line headers.
47993   */
VmHttpProcessOneHeader(SyhttpHeader * pHdr,SyhttpHeader * pLast,const char * zLine,sxu32 nLen)47994  static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen)
47995  {
47996 	 SyString *pName;
47997 	 sxu32 nPos;
47998 	 sxi32 rc;
47999 	 if( nLen < 1 ){
48000 		 return SXERR_NEXT;
48001 	 }
48002 	 /* Check for multi-line header */
48003 	if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){
48004 		SyString *pTmp = &pLast->sValue;
48005 		SyStringFullTrim(pTmp);
48006 		if( pTmp->nByte == 0 ){
48007 			SyStringInitFromBuf(pTmp, zLine, nLen);
48008 		}else{
48009 			/* Update header value length */
48010 			pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString);
48011 		}
48012 		 /* Simply tell the caller to reset its states and get another line */
48013 		 return SXERR_CONTINUE;
48014 	 }
48015 	/* Split the header */
48016 	pName = &pHdr->sName;
48017 	rc = SyByteFind(zLine, nLen, ':', &nPos);
48018 	if(rc != SXRET_OK ){
48019 		return SXERR_NEXT; /* Malformed header;Check the next entry */
48020 	}
48021 	SyStringInitFromBuf(pName, zLine, nPos);
48022 	SyStringFullTrim(pName);
48023 	/* Extract a header value */
48024 	SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1);
48025 	/* Remove leading and trailing whitespaces */
48026 	SyStringFullTrim(&pHdr->sValue);
48027 	return SXRET_OK;
48028  }
48029  /*
48030   * Extract all MIME headers associated with a HTTP request.
48031   * After processing the first line of a HTTP request, the following
48032   * routine is called in order to extract MIME headers.
48033   * This function return SXRET_OK on success, SXERR_MORE when it needs
48034   * more inputs.
48035   * Note: Any malformed header is simply discarded.
48036   */
VmHttpExtractHeaders(SyString * pRequest,SySet * pOut)48037  static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut)
48038  {
48039 	 SyhttpHeader *pLast = 0;
48040 	 SyString sCurrent;
48041 	 SyhttpHeader sHdr;
48042 	 sxu8 bEol;
48043 	 sxi32 rc;
48044 	 if( SySetUsed(pOut) > 0 ){
48045 		 pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1);
48046 	 }
48047 	 bEol = FALSE;
48048 	 for(;;){
48049 		 SyZero(&sHdr, sizeof(SyhttpHeader));
48050 		 /* Extract a single line from the raw HTTP request */
48051 		 rc = VmGetNextLine(pRequest, &sCurrent);
48052 		 if(rc != SXRET_OK ){
48053 			 if( sCurrent.nByte < 1 ){
48054 				 break;
48055 			 }
48056 			 bEol = TRUE;
48057 		 }
48058 		 /* Process the header */
48059 		 if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){
48060 			 if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){
48061 				 break;
48062 			 }
48063 			 /* Retrieve the last parsed header so we can handle multi-line header
48064 			  * in case we face one of them.
48065 			  */
48066 			 pLast = (SyhttpHeader *)SySetPeek(pOut);
48067 		 }
48068 		 if( bEol ){
48069 			 break;
48070 		 }
48071 	 } /* for(;;) */
48072 	 return SXRET_OK;
48073  }
48074  /*
48075   * Process the first line of a HTTP request.
48076   * This routine perform the following operations
48077   *  1) Extract the HTTP method.
48078   *  2) Split the request URI to it's fields [ie: host, path, query, ...].
48079   *  3) Extract the HTTP protocol version.
48080   */
VmHttpProcessFirstLine(SyString * pRequest,sxi32 * pMethod,SyhttpUri * pUri,sxi32 * pProto)48081  static sxi32 VmHttpProcessFirstLine(
48082 	 SyString *pRequest, /* Raw HTTP request */
48083 	 sxi32 *pMethod,     /* OUT: HTTP method */
48084 	 SyhttpUri *pUri,    /* OUT: Parse of the URI */
48085 	 sxi32 *pProto       /* OUT: HTTP protocol */
48086 	 )
48087  {
48088 	 static const char *azMethods[] = { "get", "post", "head", "put"};
48089 	 static const sxi32 aMethods[]  = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT};
48090 	 const char *zIn, *zEnd, *zPtr;
48091 	 SyString sLine;
48092 	 sxu32 nLen;
48093 	 sxi32 rc;
48094 	 /* Extract the first line and update the pointer */
48095 	 rc = VmGetNextLine(pRequest, &sLine);
48096 	 if( rc != SXRET_OK ){
48097 		 return rc;
48098 	 }
48099 	 if ( sLine.nByte < 1 ){
48100 		 /* Empty HTTP request */
48101 		 return SXERR_EMPTY;
48102 	 }
48103 	 /* Delimit the line and ignore trailing and leading white spaces */
48104 	 zIn = sLine.zString;
48105 	 zEnd = &zIn[sLine.nByte];
48106 	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48107 		 zIn++;
48108 	 }
48109 	 /* Extract the HTTP method */
48110 	 zPtr = zIn;
48111 	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48112 		 zIn++;
48113 	 }
48114 	 *pMethod = HTTP_METHOD_OTHR;
48115 	 if( zIn > zPtr ){
48116 		 sxu32 i;
48117 		 nLen = (sxu32)(zIn-zPtr);
48118 		 for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){
48119 			 if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){
48120 				 *pMethod = aMethods[i];
48121 				 break;
48122 			 }
48123 		 }
48124 	 }
48125 	 /* Jump trailing white spaces */
48126 	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48127 		 zIn++;
48128 	 }
48129 	  /* Extract the request URI */
48130 	 zPtr = zIn;
48131 	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48132 		 zIn++;
48133 	 }
48134 	 if( zIn > zPtr ){
48135 		 nLen = (sxu32)(zIn-zPtr);
48136 		 /* Split raw URI to it's fields */
48137 		 VmHttpSplitURI(pUri, zPtr, nLen);
48138 	 }
48139 	 /* Jump trailing white spaces */
48140 	 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
48141 		 zIn++;
48142 	 }
48143 	 /* Extract the HTTP version */
48144 	 zPtr = zIn;
48145 	 while( zIn < zEnd && !SyisSpace(zIn[0]) ){
48146 		 zIn++;
48147 	 }
48148 	 *pProto = HTTP_PROTO_11; /* HTTP/1.1 */
48149 	 rc = 1;
48150 	 if( zIn > zPtr ){
48151 		 rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr));
48152 	 }
48153 	 if( !rc ){
48154 		 *pProto = HTTP_PROTO_10; /* HTTP/1.0 */
48155 	 }
48156 	 return SXRET_OK;
48157  }
48158  /*
48159   * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded"
48160   * into a name value pair.
48161   * Note that this encoding is implicit in GET based requests.
48162   * After the tokenization process, register the decoded queries
48163   * in the $_GET/$_POST/$_REQUEST superglobals arrays.
48164   */
VmHttpSplitEncodedQuery(jx9_vm * pVm,SyString * pQuery,SyBlob * pWorker,int is_post)48165  static sxi32 VmHttpSplitEncodedQuery(
48166 	 jx9_vm *pVm,       /* Target VM */
48167 	 SyString *pQuery,  /* Raw query to decode */
48168 	 SyBlob *pWorker,   /* Working buffer */
48169 	 int is_post        /* TRUE if we are dealing with a POST request */
48170 	 )
48171  {
48172 	 const char *zEnd = &pQuery->zString[pQuery->nByte];
48173 	 const char *zIn = pQuery->zString;
48174 	 jx9_value *pGet, *pRequest;
48175 	 SyString sName, sValue;
48176 	 const char *zPtr;
48177 	 sxu32 nBlobOfft;
48178 	 /* Extract superglobals */
48179 	 if( is_post ){
48180 		 /* $_POST superglobal */
48181 		 pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1);
48182 	 }else{
48183 		 /* $_GET superglobal */
48184 		 pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1);
48185 	 }
48186 	 pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1);
48187 	 /* Split up the raw query */
48188 	 for(;;){
48189 		 /* Jump leading white spaces */
48190 		 while(zIn < zEnd  && SyisSpace(zIn[0]) ){
48191 			 zIn++;
48192 		 }
48193 		 if( zIn >= zEnd ){
48194 			 break;
48195 		 }
48196 		 zPtr = zIn;
48197 		 while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){
48198 			 zPtr++;
48199 		 }
48200 		 /* Reset the working buffer */
48201 		 SyBlobReset(pWorker);
48202 		 /* Decode the entry */
48203 		 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48204 		 /* Save the entry */
48205 		 sName.nByte = SyBlobLength(pWorker);
48206 		 sValue.zString = 0;
48207 		 sValue.nByte = 0;
48208 		 if( zPtr < zEnd && zPtr[0] == '=' ){
48209 			 zPtr++;
48210 			 zIn = zPtr;
48211 			 /* Store field value */
48212 			 while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){
48213 				 zPtr++;
48214 			 }
48215 			 if( zPtr > zIn ){
48216 				 /* Decode the value */
48217 				  nBlobOfft = SyBlobLength(pWorker);
48218 				  SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48219 				  sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft);
48220 				  sValue.nByte = SyBlobLength(pWorker) - nBlobOfft;
48221 
48222 			 }
48223 			 /* Synchronize pointers */
48224 			 zIn = zPtr;
48225 		 }
48226 		 sName.zString = (const char *)SyBlobData(pWorker);
48227 		 /* Install the decoded query in the $_GET/$_REQUEST array */
48228 		 if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){
48229 			 VmHashmapInsert((jx9_hashmap *)pGet->x.pOther,
48230 				 sName.zString, (int)sName.nByte,
48231 				 sValue.zString, (int)sValue.nByte
48232 				 );
48233 		 }
48234 		 if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
48235 			 VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther,
48236 				 sName.zString, (int)sName.nByte,
48237 				 sValue.zString, (int)sValue.nByte
48238 					 );
48239 		 }
48240 		 /* Advance the pointer */
48241 		 zIn = &zPtr[1];
48242 	 }
48243 	/* All done*/
48244 	return SXRET_OK;
48245  }
48246  /*
48247   * Extract MIME header value from the given set.
48248   * Return header value on success. NULL otherwise.
48249   */
VmHttpExtractHeaderValue(SySet * pSet,const char * zMime,sxu32 nByte)48250  static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte)
48251  {
48252 	 SyhttpHeader *aMime, *pMime;
48253 	 SyString sMime;
48254 	 sxu32 n;
48255 	 SyStringInitFromBuf(&sMime, zMime, nByte);
48256 	 /* Point to the MIME entries */
48257 	 aMime = (SyhttpHeader *)SySetBasePtr(pSet);
48258 	 /* Perform the lookup */
48259 	 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
48260 		 pMime = &aMime[n];
48261 		 if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){
48262 			 /* Header found, return it's associated value */
48263 			 return &pMime->sValue;
48264 		 }
48265 	 }
48266 	 /* No such MIME header */
48267 	 return 0;
48268  }
48269  /*
48270   * Tokenize and decode a raw "Cookie:" MIME header into a name value pair
48271   * and insert it's fields [i.e name, value] in the $_COOKIE superglobal.
48272   */
VmHttpPorcessCookie(jx9_vm * pVm,SyBlob * pWorker,const char * zIn,sxu32 nByte)48273  static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
48274  {
48275 	 const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
48276 	 SyString sName, sValue;
48277 	 jx9_value *pCookie;
48278 	 sxu32 nOfft;
48279 	 /* Make sure the $_COOKIE superglobal is available */
48280 	 pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1);
48281 	 if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){
48282 		 /* $_COOKIE superglobal not available */
48283 		 return SXERR_NOTFOUND;
48284 	 }
48285 	 for(;;){
48286 		  /* Jump leading white spaces */
48287 		 while( zIn < zEnd && SyisSpace(zIn[0]) ){
48288 			 zIn++;
48289 		 }
48290 		 if( zIn >= zEnd ){
48291 			 break;
48292 		 }
48293 		  /* Reset the working buffer */
48294 		 SyBlobReset(pWorker);
48295 		 zDelimiter = zIn;
48296 		 /* Delimit the name[=value]; pair */
48297 		 while( zDelimiter < zEnd && zDelimiter[0] != ';' ){
48298 			 zDelimiter++;
48299 		 }
48300 		 zPtr = zIn;
48301 		 while( zPtr < zDelimiter && zPtr[0] != '=' ){
48302 			 zPtr++;
48303 		 }
48304 		 /* Decode the cookie */
48305 		 SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
48306 		 sName.nByte = SyBlobLength(pWorker);
48307 		 zPtr++;
48308 		 sValue.zString = 0;
48309 		 sValue.nByte = 0;
48310 		 if( zPtr < zDelimiter ){
48311 			 /* Got a Cookie value */
48312 			 nOfft = SyBlobLength(pWorker);
48313 			 SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE);
48314 			 SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft);
48315 		 }
48316 		 /* Synchronize pointers */
48317 		 zIn = &zDelimiter[1];
48318 		 /* Perform the insertion */
48319 		 sName.zString = (const char *)SyBlobData(pWorker);
48320 		 VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther,
48321 			 sName.zString, (int)sName.nByte,
48322 			 sValue.zString, (int)sValue.nByte
48323 			 );
48324 	 }
48325 	 return SXRET_OK;
48326  }
48327  /*
48328   * Process a full HTTP request and populate the appropriate arrays
48329   * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information
48330   * extracted from the raw HTTP request. As an extension Symisc introduced
48331   * the $_HEADER array which hold a copy of the processed HTTP MIME headers
48332   * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...].
48333   * This function return SXRET_OK on success. Any other return value indicates
48334   * a malformed HTTP request.
48335   */
VmHttpProcessRequest(jx9_vm * pVm,const char * zRequest,int nByte)48336  static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte)
48337  {
48338 	 SyString *pName, *pValue, sRequest; /* Raw HTTP request */
48339 	 jx9_value *pHeaderArray;          /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/
48340 	 SyhttpHeader *pHeader;            /* MIME header */
48341 	 SyhttpUri sUri;     /* Parse of the raw URI*/
48342 	 SyBlob sWorker;     /* General purpose working buffer */
48343 	 SySet sHeader;      /* MIME headers set */
48344 	 sxi32 iMethod;      /* HTTP method [i.e: GET, POST, HEAD...]*/
48345 	 sxi32 iVer;         /* HTTP protocol version */
48346 	 sxi32 rc;
48347 	 SyStringInitFromBuf(&sRequest, zRequest, nByte);
48348 	 SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader));
48349 	 SyBlobInit(&sWorker, &pVm->sAllocator);
48350 	 /* Ignore leading and trailing white spaces*/
48351 	 SyStringFullTrim(&sRequest);
48352 	 /* Process the first line */
48353 	 rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer);
48354 	 if( rc != SXRET_OK ){
48355 		 return rc;
48356 	 }
48357 	 /* Process MIME headers */
48358 	 VmHttpExtractHeaders(&sRequest, &sHeader);
48359 	 /*
48360 	  * Setup $_SERVER environments
48361 	  */
48362 	 /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */
48363 	 jx9_vm_config(pVm,
48364 		 JX9_VM_CONFIG_SERVER_ATTR,
48365 		 "SERVER_PROTOCOL",
48366 		 iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1",
48367 		 sizeof("HTTP/1.1")-1
48368 		 );
48369 	 /* 'REQUEST_METHOD':  Which request method was used to access the page */
48370 	 jx9_vm_config(pVm,
48371 		 JX9_VM_CONFIG_SERVER_ATTR,
48372 		 "REQUEST_METHOD",
48373 		 iMethod == HTTP_METHOD_GET ?   "GET" :
48374 		 (iMethod == HTTP_METHOD_POST ? "POST":
48375 		 (iMethod == HTTP_METHOD_PUT  ? "PUT" :
48376 		 (iMethod == HTTP_METHOD_HEAD ?  "HEAD" : "OTHER"))),
48377 		 -1 /* Compute attribute length automatically */
48378 		 );
48379 	 if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){
48380 		 pValue = &sUri.sQuery;
48381 		 /* 'QUERY_STRING': The query string, if any, via which the page was accessed */
48382 		 jx9_vm_config(pVm,
48383 			 JX9_VM_CONFIG_SERVER_ATTR,
48384 			 "QUERY_STRING",
48385 			 pValue->zString,
48386 			 pValue->nByte
48387 			 );
48388 		 /* Decoded the raw query */
48389 		 VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE);
48390 	 }
48391 	 /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */
48392 	 pValue = &sUri.sRaw;
48393 	 jx9_vm_config(pVm,
48394 		 JX9_VM_CONFIG_SERVER_ATTR,
48395 		 "REQUEST_URI",
48396 		 pValue->zString,
48397 		 pValue->nByte
48398 		 );
48399 	 /*
48400 	  * 'PATH_INFO'
48401 	  * 'ORIG_PATH_INFO'
48402       * Contains any client-provided pathname information trailing the actual script filename but preceding
48403 	  * the query string, if available. For instance, if the current script was accessed via the URL
48404 	  * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
48405 	  * /some/stuff.
48406 	  */
48407 	 pValue = &sUri.sPath;
48408 	 jx9_vm_config(pVm,
48409 		 JX9_VM_CONFIG_SERVER_ATTR,
48410 		 "PATH_INFO",
48411 		 pValue->zString,
48412 		 pValue->nByte
48413 		 );
48414 	 jx9_vm_config(pVm,
48415 		 JX9_VM_CONFIG_SERVER_ATTR,
48416 		 "ORIG_PATH_INFO",
48417 		 pValue->zString,
48418 		 pValue->nByte
48419 		 );
48420 	 /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */
48421 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1);
48422 	 if( pValue ){
48423 		 jx9_vm_config(pVm,
48424 			 JX9_VM_CONFIG_SERVER_ATTR,
48425 			 "HTTP_ACCEPT",
48426 			 pValue->zString,
48427 			 pValue->nByte
48428 		 );
48429 	 }
48430 	 /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */
48431 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1);
48432 	 if( pValue ){
48433 		 jx9_vm_config(pVm,
48434 			 JX9_VM_CONFIG_SERVER_ATTR,
48435 			 "HTTP_ACCEPT_CHARSET",
48436 			 pValue->zString,
48437 			 pValue->nByte
48438 		 );
48439 	 }
48440 	 /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */
48441 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1);
48442 	 if( pValue ){
48443 		 jx9_vm_config(pVm,
48444 			 JX9_VM_CONFIG_SERVER_ATTR,
48445 			 "HTTP_ACCEPT_ENCODING",
48446 			 pValue->zString,
48447 			 pValue->nByte
48448 		 );
48449 	 }
48450 	  /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */
48451 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1);
48452 	 if( pValue ){
48453 		 jx9_vm_config(pVm,
48454 			 JX9_VM_CONFIG_SERVER_ATTR,
48455 			 "HTTP_ACCEPT_LANGUAGE",
48456 			 pValue->zString,
48457 			 pValue->nByte
48458 		 );
48459 	 }
48460 	 /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */
48461 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1);
48462 	 if( pValue ){
48463 		 jx9_vm_config(pVm,
48464 			 JX9_VM_CONFIG_SERVER_ATTR,
48465 			 "HTTP_CONNECTION",
48466 			 pValue->zString,
48467 			 pValue->nByte
48468 		 );
48469 	 }
48470 	 /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */
48471 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1);
48472 	 if( pValue ){
48473 		 jx9_vm_config(pVm,
48474 			 JX9_VM_CONFIG_SERVER_ATTR,
48475 			 "HTTP_HOST",
48476 			 pValue->zString,
48477 			 pValue->nByte
48478 		 );
48479 	 }
48480 	 /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */
48481 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1);
48482 	 if( pValue ){
48483 		 jx9_vm_config(pVm,
48484 			 JX9_VM_CONFIG_SERVER_ATTR,
48485 			 "HTTP_REFERER",
48486 			 pValue->zString,
48487 			 pValue->nByte
48488 		 );
48489 	 }
48490 	 /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */
48491 	 pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1);
48492 	 if( pValue ){
48493 		 jx9_vm_config(pVm,
48494 			 JX9_VM_CONFIG_SERVER_ATTR,
48495 			 "HTTP_USER_AGENT",
48496 			 pValue->zString,
48497 			 pValue->nByte
48498 		 );
48499 	 }
48500 	  /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization'
48501 	   * header sent by the client (which you should then use to make the appropriate validation).
48502 	   */
48503 	 pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1);
48504 	 if( pValue ){
48505 		 jx9_vm_config(pVm,
48506 			 JX9_VM_CONFIG_SERVER_ATTR,
48507 			 "JX9_AUTH_DIGEST",
48508 			 pValue->zString,
48509 			 pValue->nByte
48510 		 );
48511 		 jx9_vm_config(pVm,
48512 			 JX9_VM_CONFIG_SERVER_ATTR,
48513 			 "JX9_AUTH",
48514 			 pValue->zString,
48515 			 pValue->nByte
48516 		 );
48517 	 }
48518 	 /* Install all clients HTTP headers in the $_HEADER superglobal */
48519 	 pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1);
48520 	 /* Iterate throw the available MIME headers*/
48521 	 SySetResetCursor(&sHeader);
48522 	 pHeader = 0; /* stupid cc warning */
48523 	 while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){
48524 		 pName  = &pHeader->sName;
48525 		 pValue = &pHeader->sValue;
48526 		 if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){
48527 			 /* Insert the MIME header and it's associated value */
48528 			 VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther,
48529 				 pName->zString, (int)pName->nByte,
48530 				 pValue->zString, (int)pValue->nByte
48531 				 );
48532 		 }
48533 		 if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0
48534 			 && pValue->nByte > 0){
48535 				 /* Process the name=value pair and insert them in the $_COOKIE superglobal array */
48536 				 VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte);
48537 		 }
48538 	 }
48539 	 if( iMethod == HTTP_METHOD_POST ){
48540 		 /* Extract raw POST data */
48541 		 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1);
48542 		 if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 &&
48543 			 SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){
48544 				 /* Extract POST data length */
48545 				 pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1);
48546 				 if( pValue ){
48547 					 sxi32 iLen = 0; /* POST data length */
48548 					 SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0);
48549 					 if( iLen > 0 ){
48550 						 /* Remove leading and trailing white spaces */
48551 						 SyStringFullTrim(&sRequest);
48552 						 if( (int)sRequest.nByte > iLen ){
48553 							 sRequest.nByte = (sxu32)iLen;
48554 						 }
48555 						 /* Decode POST data now */
48556 						 VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE);
48557 					 }
48558 				 }
48559 		 }
48560 	 }
48561 	 /* All done, clean-up the mess left behind */
48562 	 SySetRelease(&sHeader);
48563 	 SyBlobRelease(&sWorker);
48564 	 return SXRET_OK;
48565  }
48566 
48567 /* lhash_kv.c */
48568 /*
48569  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
48570  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
48571  * Copyright (C) 2014, Yuras Shumovich <shumovichy@gmail.com>
48572  * Version 1.1.6
48573  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
48574  * please contact Symisc Systems via:
48575  *       legal@symisc.net
48576  *       licensing@symisc.net
48577  *       contact@symisc.net
48578  * or visit:
48579  *      http://unqlite.org/licensing.html
48580  */
48581  /* $SymiscID: lhash_kv.c v1.7 Solaris 2013-01-14 12:56 stable <chm@symisc.net> $ */
48582 #ifndef UNQLITE_AMALGAMATION
48583 #include "unqliteInt.h"
48584 #endif
48585 /*
48586  * This file implements disk based hashtable using the linear hashing algorithm.
48587  * This implementation is the one decribed in the paper:
48588  *  LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France.
48589  * Plus a smart extension called Virtual Bucket Table. (contact devel@symisc.net for additional information).
48590  */
48591 /* Magic number identifying a valid storage image */
48592 #define L_HASH_MAGIC 0xFA782DCB
48593 /*
48594  * Magic word to hash to identify a valid hash function.
48595  */
48596 #define L_HASH_WORD "chm@symisc"
48597 /*
48598  * Cell size on disk.
48599  */
48600 #define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/)
48601 /*
48602  * Primary page (not overflow pages) header size on disk.
48603  */
48604 #define L_HASH_PAGE_HDR_SZ (2/* Cell offset*/+2/* Free block offset*/+8/*Slave page number*/)
48605 /*
48606  * The maximum amount of payload (in bytes) that can be stored locally for
48607  * a database entry.  If the entry contains more data than this, the
48608  * extra goes onto overflow pages.
48609 */
48610 #define L_HASH_MX_PAYLOAD(PageSize)  (PageSize-(L_HASH_PAGE_HDR_SZ+L_HASH_CELL_SZ))
48611 /*
48612  * Maxium free space on a single page.
48613  */
48614 #define L_HASH_MX_FREE_SPACE(PageSize) (PageSize - (L_HASH_PAGE_HDR_SZ))
48615 /*
48616 ** The maximum number of bytes of payload allowed on a single overflow page.
48617 */
48618 #define L_HASH_OVERFLOW_SIZE(PageSize) (PageSize-8)
48619 /* Forward declaration */
48620 typedef struct lhash_kv_engine lhash_kv_engine;
48621 typedef struct lhpage lhpage;
48622 /*
48623  * Each record in the database is identified either in-memory or in
48624  * disk by an instance of the following structure.
48625  */
48626 typedef struct lhcell lhcell;
48627 struct lhcell
48628 {
48629 	/* Disk-data (Big-Endian) */
48630 	sxu32 nHash;   /* Hash of the key: 4 bytes */
48631 	sxu32 nKey;    /* Key length: 4 bytes */
48632 	sxu64 nData;   /* Data length: 8 bytes */
48633 	sxu16 iNext;   /* Offset of the next cell: 2 bytes */
48634 	pgno iOvfl;    /* Overflow page number if any: 8 bytes */
48635 	/* In-memory data only */
48636 	lhpage *pPage;     /* Page this cell belongs */
48637 	sxu16 iStart;      /* Offset of this cell */
48638 	pgno iDataPage;    /* Data page number when overflow */
48639 	sxu16 iDataOfft;   /* Offset of the data in iDataPage */
48640 	SyBlob sKey;       /* Record key for fast lookup (Kept in-memory if < 256KB ) */
48641 	lhcell *pNext,*pPrev;         /* Linked list of the loaded memory cells */
48642 	lhcell *pNextCol,*pPrevCol;   /* Collison chain  */
48643 };
48644 /*
48645 ** Each database page has a header that is an instance of this
48646 ** structure.
48647 */
48648 typedef struct lhphdr lhphdr;
48649 struct lhphdr
48650 {
48651   sxu16 iOfft; /* Offset of the first cell */
48652   sxu16 iFree; /* Offset of the first free block*/
48653   pgno iSlave; /* Slave page number */
48654 };
48655 /*
48656  * Each loaded primary disk page is represented in-memory using
48657  * an instance of the following structure.
48658  */
48659 struct lhpage
48660 {
48661 	lhash_kv_engine *pHash;  /* KV Storage engine that own this page */
48662 	unqlite_page *pRaw;      /* Raw page contents */
48663 	lhphdr sHdr;             /* Processed page header */
48664 	lhcell **apCell;         /* Cell buckets */
48665 	lhcell *pList,*pFirst;   /* Linked list of cells */
48666 	sxu32 nCell;             /* Total number of cells */
48667 	sxu32 nCellSize;         /* apCell[] size */
48668 	lhpage *pMaster;         /* Master page in case we are dealing with a slave page */
48669 	lhpage *pSlave;          /* List of slave pages */
48670 	lhpage *pNextSlave;      /* Next slave page on the list */
48671 	sxi32 iSlave;            /* Total number of slave pages */
48672 	sxu16 nFree;             /* Amount of free space available in the page */
48673 };
48674 /*
48675  * A Bucket map record which is used to map logical bucket number to real
48676  * bucket number is represented by an instance of the following structure.
48677  */
48678 typedef struct lhash_bmap_rec lhash_bmap_rec;
48679 struct lhash_bmap_rec
48680 {
48681 	pgno iLogic;                   /* Logical bucket number */
48682 	pgno iReal;                    /* Real bucket number */
48683 	lhash_bmap_rec *pNext,*pPrev;  /* Link to other bucket map */
48684 	lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */
48685 };
48686 typedef struct lhash_bmap_page lhash_bmap_page;
48687 struct lhash_bmap_page
48688 {
48689 	pgno iNum;   /* Page number where this entry is stored */
48690 	sxu16 iPtr;  /* Offset to start reading/writing from */
48691 	sxu32 nRec;  /* Total number of records in this page */
48692 	pgno iNext;  /* Next map page */
48693 };
48694 /*
48695  * An in memory linear hash implemenation is represented by in an isntance
48696  * of the following structure.
48697  */
48698 struct lhash_kv_engine
48699 {
48700 	const unqlite_kv_io *pIo;     /* IO methods: Must be first */
48701 	/* Private fields */
48702 	SyMemBackend sAllocator;      /* Private memory backend */
48703 	ProcHash xHash;               /* Default hash function */
48704 	ProcCmp xCmp;                 /* Default comparison function */
48705 	unqlite_page *pHeader;        /* Page one to identify a valid implementation */
48706 	lhash_bmap_rec **apMap;       /* Buckets map records */
48707 	sxu32 nBuckRec;               /* Total number of bucket map records */
48708 	sxu32 nBuckSize;              /* apMap[] size  */
48709 	lhash_bmap_rec *pList;        /* List of bucket map records */
48710 	lhash_bmap_rec *pFirst;       /* First record*/
48711 	lhash_bmap_page sPageMap;     /* Primary bucket map */
48712 	int iPageSize;                /* Page size */
48713 	pgno nFreeList;               /* List of free pages */
48714 	pgno split_bucket;            /* Current split bucket: MUST BE A POWER OF TWO */
48715 	pgno max_split_bucket;        /* Maximum split bucket: MUST BE A POWER OF TWO */
48716 	pgno nmax_split_nucket;       /* Next maximum split bucket (1 << nMsb): In-memory only */
48717 	sxu32 nMagic;                 /* Magic number to identify a valid linear hash disk database */
48718 };
48719 /*
48720  * Given a logical bucket number, return the record associated with it.
48721  */
lhMapFindBucket(lhash_kv_engine * pEngine,pgno iLogic)48722 static lhash_bmap_rec * lhMapFindBucket(lhash_kv_engine *pEngine,pgno iLogic)
48723 {
48724 	lhash_bmap_rec *pRec;
48725 	if( pEngine->nBuckRec < 1 ){
48726 		/* Don't bother */
48727 		return 0;
48728 	}
48729 	pRec = pEngine->apMap[iLogic & (pEngine->nBuckSize - 1)];
48730 	for(;;){
48731 		if( pRec == 0 ){
48732 			break;
48733 		}
48734 		if( pRec->iLogic == iLogic ){
48735 			return pRec;
48736 		}
48737 		/* Point to the next entry */
48738 		pRec = pRec->pNextCol;
48739 	}
48740 	/* No such record */
48741 	return 0;
48742 }
48743 /*
48744  * Install a new bucket map record.
48745  */
lhMapInstallBucket(lhash_kv_engine * pEngine,pgno iLogic,pgno iReal)48746 static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
48747 {
48748 	lhash_bmap_rec *pRec;
48749 	sxu32 iBucket;
48750 	/* Allocate a new instance */
48751 	pRec = (lhash_bmap_rec *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhash_bmap_rec));
48752 	if( pRec == 0 ){
48753 		return UNQLITE_NOMEM;
48754 	}
48755 	/* Zero the structure */
48756 	SyZero(pRec,sizeof(lhash_bmap_rec));
48757 	/* Fill in the structure */
48758 	pRec->iLogic = iLogic;
48759 	pRec->iReal = iReal;
48760 	iBucket = iLogic & (pEngine->nBuckSize - 1);
48761 	pRec->pNextCol = pEngine->apMap[iBucket];
48762 	if( pEngine->apMap[iBucket] ){
48763 		pEngine->apMap[iBucket]->pPrevCol = pRec;
48764 	}
48765 	pEngine->apMap[iBucket] = pRec;
48766 	/* Link */
48767 	if( pEngine->pFirst == 0 ){
48768 		pEngine->pFirst = pEngine->pList = pRec;
48769 	}else{
48770 		MACRO_LD_PUSH(pEngine->pList,pRec);
48771 	}
48772 	pEngine->nBuckRec++;
48773 	if( (pEngine->nBuckRec >= pEngine->nBuckSize * 3) && pEngine->nBuckRec < 100000 ){
48774 		/* Allocate a new larger table */
48775 		sxu32 nNewSize = pEngine->nBuckSize << 1;
48776 		lhash_bmap_rec *pEntry;
48777 		lhash_bmap_rec **apNew;
48778 		sxu32 n;
48779 
48780 		apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *));
48781 		if( apNew ){
48782 			/* Zero the new table */
48783 			SyZero((void *)apNew, nNewSize * sizeof(lhash_bmap_rec *));
48784 			/* Rehash all entries */
48785 			n = 0;
48786 			pEntry = pEngine->pList;
48787 			for(;;){
48788 				/* Loop one */
48789 				if( n >= pEngine->nBuckRec ){
48790 					break;
48791 				}
48792 				pEntry->pNextCol = pEntry->pPrevCol = 0;
48793 				/* Install in the new bucket */
48794 				iBucket = pEntry->iLogic & (nNewSize - 1);
48795 				pEntry->pNextCol = apNew[iBucket];
48796 				if( apNew[iBucket] ){
48797 					apNew[iBucket]->pPrevCol = pEntry;
48798 				}
48799 				apNew[iBucket] = pEntry;
48800 				/* Point to the next entry */
48801 				pEntry = pEntry->pNext;
48802 				n++;
48803 			}
48804 			/* Release the old table and reflect the change */
48805 			SyMemBackendFree(&pEngine->sAllocator,(void *)pEngine->apMap);
48806 			pEngine->apMap = apNew;
48807 			pEngine->nBuckSize  = nNewSize;
48808 		}
48809 	}
48810 	return UNQLITE_OK;
48811 }
48812 /*
48813  * Process a raw bucket map record.
48814  */
lhMapLoadPage(lhash_kv_engine * pEngine,lhash_bmap_page * pMap,const unsigned char * zRaw)48815 static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const unsigned char *zRaw)
48816 {
48817 	const unsigned char *zEnd = &zRaw[pEngine->iPageSize];
48818 	const unsigned char *zPtr = zRaw;
48819 	pgno iLogic,iReal;
48820 	sxu32 n;
48821 	int rc;
48822 	if( pMap->iPtr == 0 ){
48823 		/* Read the map header */
48824 		SyBigEndianUnpack64(zRaw,&pMap->iNext);
48825 		zRaw += 8;
48826 		SyBigEndianUnpack32(zRaw,&pMap->nRec);
48827 		zRaw += 4;
48828 	}else{
48829 		/* Mostly page one of the database */
48830 		zRaw += pMap->iPtr;
48831 	}
48832 	/* Start processing */
48833 	for( n = 0; n < pMap->nRec ; ++n ){
48834 		if( zRaw >= zEnd ){
48835 			break;
48836 		}
48837 		/* Extract the logical and real bucket number */
48838 		SyBigEndianUnpack64(zRaw,&iLogic);
48839 		zRaw += 8;
48840 		SyBigEndianUnpack64(zRaw,&iReal);
48841 		zRaw += 8;
48842 		/* Install the record in the map */
48843 		rc = lhMapInstallBucket(pEngine,iLogic,iReal);
48844 		if( rc != UNQLITE_OK ){
48845 			return rc;
48846 		}
48847 	}
48848 	pMap->iPtr = (sxu16)(zRaw-zPtr);
48849 	/* All done */
48850 	return UNQLITE_OK;
48851 }
48852 /*
48853  * Allocate a new cell instance.
48854  */
lhNewCell(lhash_kv_engine * pEngine,lhpage * pPage)48855 static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage)
48856 {
48857 	lhcell *pCell;
48858 	pCell = (lhcell *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhcell));
48859 	if( pCell == 0 ){
48860 		return 0;
48861 	}
48862 	/* Zero the structure */
48863 	SyZero(pCell,sizeof(lhcell));
48864 	/* Fill in the structure */
48865 	SyBlobInit(&pCell->sKey,&pEngine->sAllocator);
48866 	pCell->pPage = pPage;
48867 	return pCell;
48868 }
48869 /*
48870  * Discard a cell from the page table.
48871  */
lhCellDiscard(lhcell * pCell)48872 static void lhCellDiscard(lhcell *pCell)
48873 {
48874 	lhpage *pPage = pCell->pPage->pMaster;
48875 
48876 	if( pCell->pPrevCol ){
48877 		pCell->pPrevCol->pNextCol = pCell->pNextCol;
48878 	}else{
48879 		pPage->apCell[pCell->nHash & (pPage->nCellSize - 1)] = pCell->pNextCol;
48880 	}
48881 	if( pCell->pNextCol ){
48882 		pCell->pNextCol->pPrevCol = pCell->pPrevCol;
48883 	}
48884 	MACRO_LD_REMOVE(pPage->pList,pCell);
48885 	if( pCell == pPage->pFirst ){
48886 		pPage->pFirst = pCell->pPrev;
48887 	}
48888 	pPage->nCell--;
48889 	/* Release the cell */
48890 	SyBlobRelease(&pCell->sKey);
48891 	SyMemBackendPoolFree(&pPage->pHash->sAllocator,pCell);
48892 }
48893 /*
48894  * Install a cell in the page table.
48895  */
lhInstallCell(lhcell * pCell)48896 static int lhInstallCell(lhcell *pCell)
48897 {
48898 	lhpage *pPage = pCell->pPage->pMaster;
48899 	sxu32 iBucket;
48900 	if( pPage->nCell < 1 ){
48901 		sxu32 nTableSize = 32; /* Must be a power of two */
48902 		lhcell **apTable;
48903 		/* Allocate a new cell table */
48904 		apTable = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nTableSize * sizeof(lhcell *));
48905 		if( apTable == 0 ){
48906 			return UNQLITE_NOMEM;
48907 		}
48908 		/* Zero the new table */
48909 		SyZero((void *)apTable, nTableSize * sizeof(lhcell *));
48910 		/* Install it */
48911 		pPage->apCell = apTable;
48912 		pPage->nCellSize = nTableSize;
48913 	}
48914 	iBucket = pCell->nHash & (pPage->nCellSize - 1);
48915 	pCell->pNextCol = pPage->apCell[iBucket];
48916 	if( pPage->apCell[iBucket] ){
48917 		pPage->apCell[iBucket]->pPrevCol = pCell;
48918 	}
48919 	pPage->apCell[iBucket] = pCell;
48920 	if( pPage->pFirst == 0 ){
48921 		pPage->pFirst = pPage->pList = pCell;
48922 	}else{
48923 		MACRO_LD_PUSH(pPage->pList,pCell);
48924 	}
48925 	pPage->nCell++;
48926 	if( (pPage->nCell >= pPage->nCellSize * 3) && pPage->nCell < 100000 ){
48927 		/* Allocate a new larger table */
48928 		sxu32 nNewSize = pPage->nCellSize << 1;
48929 		lhcell *pEntry;
48930 		lhcell **apNew;
48931 		sxu32 n;
48932 
48933 		apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *));
48934 		if( apNew ){
48935 			/* Zero the new table */
48936 			SyZero((void *)apNew, nNewSize * sizeof(lhcell *));
48937 			/* Rehash all entries */
48938 			n = 0;
48939 			pEntry = pPage->pList;
48940 			for(;;){
48941 				/* Loop one */
48942 				if( n >= pPage->nCell ){
48943 					break;
48944 				}
48945 				pEntry->pNextCol = pEntry->pPrevCol = 0;
48946 				/* Install in the new bucket */
48947 				iBucket = pEntry->nHash & (nNewSize - 1);
48948 				pEntry->pNextCol = apNew[iBucket];
48949 				if( apNew[iBucket]  ){
48950 					apNew[iBucket]->pPrevCol = pEntry;
48951 				}
48952 				apNew[iBucket] = pEntry;
48953 				/* Point to the next entry */
48954 				pEntry = pEntry->pNext;
48955 				n++;
48956 			}
48957 			/* Release the old table and reflect the change */
48958 			SyMemBackendFree(&pPage->pHash->sAllocator,(void *)pPage->apCell);
48959 			pPage->apCell = apNew;
48960 			pPage->nCellSize  = nNewSize;
48961 		}
48962 	}
48963 	return UNQLITE_OK;
48964 }
48965 /*
48966  * Private data of lhKeyCmp().
48967  */
48968 struct lhash_key_cmp
48969 {
48970 	const char *zIn;  /* Start of the stream */
48971 	const char *zEnd; /* End of the stream */
48972 	ProcCmp xCmp;     /* Comparison function */
48973 };
48974 /*
48975  * Comparsion callback for large key > 256 KB
48976  */
lhKeyCmp(const void * pData,sxu32 nLen,void * pUserData)48977 static int lhKeyCmp(const void *pData,sxu32 nLen,void *pUserData)
48978 {
48979 	struct lhash_key_cmp *pCmp = (struct lhash_key_cmp *)pUserData;
48980 	int rc;
48981 	if( pCmp->zIn >= pCmp->zEnd ){
48982 		if( nLen > 0 ){
48983 			return UNQLITE_ABORT;
48984 		}
48985 		return UNQLITE_OK;
48986 	}
48987 	/* Perform the comparison */
48988 	rc = pCmp->xCmp((const void *)pCmp->zIn,pData,nLen);
48989 	if( rc != 0 ){
48990 		/* Abort comparison */
48991 		return UNQLITE_ABORT;
48992 	}
48993 	/* Advance the cursor */
48994 	pCmp->zIn += nLen;
48995 	return UNQLITE_OK;
48996 }
48997 /* Forward declaration */
48998 static int lhConsumeCellkey(lhcell *pCell,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData,int offt_only);
48999 /*
49000  * given a key, return the cell associated with it on success. NULL otherwise.
49001  */
lhFindCell(lhpage * pPage,const void * pKey,sxu32 nByte,sxu32 nHash)49002 static lhcell * lhFindCell(
49003 	lhpage *pPage,    /* Target page */
49004 	const void *pKey, /* Lookup key */
49005 	sxu32 nByte,      /* Key length */
49006 	sxu32 nHash       /* Hash of the key */
49007 	)
49008 {
49009 	lhcell *pEntry;
49010 	if( pPage->nCell < 1 ){
49011 		/* Don't bother hashing */
49012 		return 0;
49013 	}
49014 	/* Point to the corresponding bucket */
49015 	pEntry = pPage->apCell[nHash & (pPage->nCellSize - 1)];
49016 	for(;;){
49017 		if( pEntry == 0 ){
49018 			break;
49019 		}
49020 		if( pEntry->nHash == nHash && pEntry->nKey == nByte ){
49021 			if( SyBlobLength(&pEntry->sKey) < 1 ){
49022 				/* Large key (> 256 KB) are not kept in-memory */
49023 				struct lhash_key_cmp sCmp;
49024 				int rc;
49025 				/* Fill-in the structure */
49026 				sCmp.zIn = (const char *)pKey;
49027 				sCmp.zEnd = &sCmp.zIn[nByte];
49028 				sCmp.xCmp = pPage->pHash->xCmp;
49029 				/* Fetch the key from disk and perform the comparison */
49030 				rc = lhConsumeCellkey(pEntry,lhKeyCmp,&sCmp,0);
49031 				if( rc == UNQLITE_OK ){
49032 					/* Cell found */
49033 					return pEntry;
49034 				}
49035 			}else if ( pPage->pHash->xCmp(pKey,SyBlobData(&pEntry->sKey),nByte) == 0 ){
49036 				/* Cell found */
49037 				return pEntry;
49038 			}
49039 		}
49040 		/* Point to the next entry */
49041 		pEntry = pEntry->pNextCol;
49042 	}
49043 	/* No such entry */
49044 	return 0;
49045 }
49046 /*
49047  * Parse a raw cell fetched from disk.
49048  */
lhParseOneCell(lhpage * pPage,const unsigned char * zRaw,const unsigned char * zEnd,lhcell ** ppOut)49049 static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned char *zEnd,lhcell **ppOut)
49050 {
49051 	sxu16 iNext,iOfft;
49052 	sxu32 iHash,nKey;
49053 	lhcell *pCell;
49054 	sxu64 nData;
49055 	int rc;
49056 	/* Offset this cell is stored */
49057 	iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData);
49058 	/* 4 byte hash number */
49059 	SyBigEndianUnpack32(zRaw,&iHash);
49060 	zRaw += 4;
49061 	/* 4 byte key length  */
49062 	SyBigEndianUnpack32(zRaw,&nKey);
49063 	zRaw += 4;
49064 	/* 8 byte data length */
49065 	SyBigEndianUnpack64(zRaw,&nData);
49066 	zRaw += 8;
49067 	/* 2 byte offset of the next cell */
49068 	SyBigEndianUnpack16(zRaw,&iNext);
49069 	/* Perform a sanity check */
49070 	if( iNext > 0 && &pPage->pRaw->zData[iNext] >= zEnd ){
49071 		return UNQLITE_CORRUPT;
49072 	}
49073 	zRaw += 2;
49074 	pCell = lhNewCell(pPage->pHash,pPage);
49075 	if( pCell == 0 ){
49076 		return UNQLITE_NOMEM;
49077 	}
49078 	/* Fill in the structure */
49079 	pCell->iNext = iNext;
49080 	pCell->nKey  = nKey;
49081 	pCell->nData = nData;
49082 	pCell->nHash = iHash;
49083 	/* Overflow page if any */
49084 	SyBigEndianUnpack64(zRaw,&pCell->iOvfl);
49085 	zRaw += 8;
49086 	/* Cell offset */
49087 	pCell->iStart = iOfft;
49088 	/* Consume the key */
49089 	rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,pCell->nKey > 262144 /* 256 KB */? 1 : 0);
49090 	if( rc != UNQLITE_OK ){
49091 		/* TICKET: 14-32-chm@symisc.net: Key too large for memory */
49092 		SyBlobRelease(&pCell->sKey);
49093 	}
49094 	/* Finally install the cell */
49095 	rc = lhInstallCell(pCell);
49096 	if( rc != UNQLITE_OK ){
49097 		return rc;
49098 	}
49099 	if( ppOut ){
49100 		*ppOut = pCell;
49101 	}
49102 	return UNQLITE_OK;
49103 }
49104 /*
49105  * Compute the total number of free space on a given page.
49106  */
lhPageFreeSpace(lhpage * pPage)49107 static int lhPageFreeSpace(lhpage *pPage)
49108 {
49109 	const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
49110 	lhphdr *pHdr = &pPage->sHdr;
49111 	sxu16 iNext,iAmount;
49112 	sxu16 nFree = 0;
49113 	if( pHdr->iFree < 1 ){
49114 		/* Don't bother processing, the page is full */
49115 		pPage->nFree = 0;
49116 		return UNQLITE_OK;
49117 	}
49118 	/* Point to first free block */
49119 	zEnd = &zRaw[pPage->pHash->iPageSize];
49120 	zRaw += pHdr->iFree;
49121 	for(;;){
49122 		/* Offset of the next free block */
49123 		SyBigEndianUnpack16(zRaw,&iNext);
49124 		zRaw += 2;
49125 		/* Available space on this block */
49126 		SyBigEndianUnpack16(zRaw,&iAmount);
49127 		nFree += iAmount;
49128 		if( iNext < 1 ){
49129 			/* No more free blocks */
49130 			break;
49131 		}
49132 		/* Point to the next free block*/
49133 		zRaw = &pPage->pRaw->zData[iNext];
49134 		if( zRaw >= zEnd ){
49135 			/* Corrupt page */
49136 			return UNQLITE_CORRUPT;
49137 		}
49138 	}
49139 	/* Save the amount of free space */
49140 	pPage->nFree = nFree;
49141 	return UNQLITE_OK;
49142 }
49143 /*
49144  * Given a primary page, load all its cell.
49145  */
lhLoadCells(lhpage * pPage)49146 static int lhLoadCells(lhpage *pPage)
49147 {
49148 	const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
49149 	lhphdr *pHdr = &pPage->sHdr;
49150 	lhcell *pCell = 0; /* cc warning */
49151 	int rc;
49152 	/* Calculate the amount of free space available first */
49153 	rc = lhPageFreeSpace(pPage);
49154 	if( rc != UNQLITE_OK ){
49155 		return rc;
49156 	}
49157 	if( pHdr->iOfft < 1 ){
49158 		/* Don't bother processing, the page is empty */
49159 		return UNQLITE_OK;
49160 	}
49161 	/* Point to first cell */
49162 	zRaw += pHdr->iOfft;
49163 	zEnd = &zRaw[pPage->pHash->iPageSize];
49164 	for(;;){
49165 		/* Parse a single cell */
49166 		rc = lhParseOneCell(pPage,zRaw,zEnd,&pCell);
49167 		if( rc != UNQLITE_OK ){
49168 			return rc;
49169 		}
49170 		if( pCell->iNext < 1 ){
49171 			/* No more cells */
49172 			break;
49173 		}
49174 		/* Point to the next cell */
49175 		zRaw = &pPage->pRaw->zData[pCell->iNext];
49176 		if( zRaw >= zEnd ){
49177 			/* Corrupt page */
49178 			return UNQLITE_CORRUPT;
49179 		}
49180 	}
49181 	/* All done */
49182 	return UNQLITE_OK;
49183 }
49184 /*
49185  * Given a page, parse its raw headers.
49186  */
lhParsePageHeader(lhpage * pPage)49187 static int lhParsePageHeader(lhpage *pPage)
49188 {
49189 	const unsigned char *zRaw = pPage->pRaw->zData;
49190 	lhphdr *pHdr = &pPage->sHdr;
49191 	/* Offset of the first cell */
49192 	SyBigEndianUnpack16(zRaw,&pHdr->iOfft);
49193 	zRaw += 2;
49194 	/* Offset of the first free block */
49195 	SyBigEndianUnpack16(zRaw,&pHdr->iFree);
49196 	zRaw += 2;
49197 	/* Slave page number */
49198 	SyBigEndianUnpack64(zRaw,&pHdr->iSlave);
49199 	/* All done */
49200 	return UNQLITE_OK;
49201 }
49202 /*
49203  * Allocate a new page instance.
49204  */
lhNewPage(lhash_kv_engine * pEngine,unqlite_page * pRaw,lhpage * pMaster)49205 static lhpage * lhNewPage(
49206 	lhash_kv_engine *pEngine, /* KV store which own this instance */
49207 	unqlite_page *pRaw,       /* Raw page contents */
49208 	lhpage *pMaster           /* Master page in case we are dealing with a slave page */
49209 	)
49210 {
49211 	lhpage *pPage;
49212 	/* Allocate a new instance */
49213 	pPage = (lhpage *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhpage));
49214 	if( pPage == 0 ){
49215 		return 0;
49216 	}
49217 	/* Zero the structure */
49218 	SyZero(pPage,sizeof(lhpage));
49219 	/* Fill-in the structure */
49220 	pPage->pHash = pEngine;
49221 	pPage->pRaw = pRaw;
49222 	pPage->pMaster = pMaster ? pMaster /* Slave page */ : pPage /* Master page */ ;
49223 	if( pPage->pMaster != pPage ){
49224 		/* Slave page, attach it to its master */
49225 		pPage->pNextSlave = pMaster->pSlave;
49226 		pMaster->pSlave = pPage;
49227 		pMaster->iSlave++;
49228 	}
49229 	/* Save this instance for future fast lookup */
49230 	pRaw->pUserData = pPage;
49231 	/* All done */
49232 	return pPage;
49233 }
49234 /*
49235  * Load a primary and its associated slave pages from disk.
49236  */
lhLoadPage(lhash_kv_engine * pEngine,pgno pnum,lhpage * pMaster,lhpage ** ppOut,int iNest)49237 static int lhLoadPage(lhash_kv_engine *pEngine,pgno pnum,lhpage *pMaster,lhpage **ppOut,int iNest)
49238 {
49239 	unqlite_page *pRaw;
49240 	lhpage *pPage = 0; /* cc warning */
49241 	int rc;
49242 	/* Aquire the page from the pager first */
49243 	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pnum,&pRaw);
49244 	if( rc != UNQLITE_OK ){
49245 		return rc;
49246 	}
49247 	if( pRaw->pUserData ){
49248 		/* The page is already parsed and loaded in memory. Point to it */
49249 		pPage = (lhpage *)pRaw->pUserData;
49250 	}else{
49251 		/* Allocate a new page */
49252 		pPage = lhNewPage(pEngine,pRaw,pMaster);
49253 		if( pPage == 0 ){
49254 			return UNQLITE_NOMEM;
49255 		}
49256 		/* Process the page */
49257 		rc = lhParsePageHeader(pPage);
49258 		if( rc == UNQLITE_OK ){
49259 			/* Load cells */
49260 			rc = lhLoadCells(pPage);
49261 		}
49262 		if( rc != UNQLITE_OK ){
49263 			pEngine->pIo->xPageUnref(pPage->pRaw); /* pPage will be released inside this call */
49264 			return rc;
49265 		}
49266 		if( pPage->sHdr.iSlave > 0 && iNest < 128 ){
49267 			if( pMaster == 0 ){
49268 				pMaster = pPage;
49269 			}
49270 			/* Slave page. Not a fatal error if something goes wrong here */
49271 			lhLoadPage(pEngine,pPage->sHdr.iSlave,pMaster,0,iNest++);
49272 		}
49273 	}
49274 	if( ppOut ){
49275 		*ppOut = pPage;
49276 	}
49277 	return UNQLITE_OK;
49278 }
49279 /*
49280  * Given a cell, Consume its key by invoking the given callback for each extracted chunk.
49281  */
lhConsumeCellkey(lhcell * pCell,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData,int offt_only)49282 static int lhConsumeCellkey(
49283 	lhcell *pCell, /* Target cell */
49284 	int (*xConsumer)(const void *,unsigned int,void *), /* Consumer callback */
49285 	void *pUserData, /* Last argument to xConsumer() */
49286 	int offt_only
49287 	)
49288 {
49289 	lhpage *pPage = pCell->pPage;
49290 	const unsigned char *zRaw = pPage->pRaw->zData;
49291 	const unsigned char *zPayload;
49292 	int rc;
49293 	/* Point to the payload area */
49294 	zPayload = &zRaw[pCell->iStart];
49295 	if( pCell->iOvfl == 0 ){
49296 		/* Best scenario, consume the key directly without any overflow page */
49297 		zPayload += L_HASH_CELL_SZ;
49298 		rc = xConsumer((const void *)zPayload,pCell->nKey,pUserData);
49299 		if( rc != UNQLITE_OK ){
49300 			rc = UNQLITE_ABORT;
49301 		}
49302 	}else{
49303 		lhash_kv_engine *pEngine = pPage->pHash;
49304 		sxu32 nByte,nData = pCell->nKey;
49305 		unqlite_page *pOvfl;
49306 		int data_offset = 0;
49307 		pgno iOvfl;
49308 		/* Overflow page */
49309 		iOvfl = pCell->iOvfl;
49310 		/* Total usable bytes in an overflow page */
49311 		nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
49312 		for(;;){
49313 			if( iOvfl == 0 || nData < 1 ){
49314 				/* no more overflow page */
49315 				break;
49316 			}
49317 			/* Point to the overflow page */
49318 			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
49319 			if( rc != UNQLITE_OK ){
49320 				return rc;
49321 			}
49322 			zPayload = &pOvfl->zData[8];
49323 			/* Point to the raw content */
49324 			if( !data_offset ){
49325 				/* Get the data page and offset */
49326 				SyBigEndianUnpack64(zPayload,&pCell->iDataPage);
49327 				zPayload += 8;
49328 				SyBigEndianUnpack16(zPayload,&pCell->iDataOfft);
49329 				zPayload += 2;
49330 				if( offt_only ){
49331 					/* Key too large, grab the data offset and return */
49332 					pEngine->pIo->xPageUnref(pOvfl);
49333 					return UNQLITE_OK;
49334 				}
49335 				data_offset = 1;
49336 			}
49337 			/* Consume the key */
49338 			if( nData <= nByte ){
49339 				rc = xConsumer((const void *)zPayload,nData,pUserData);
49340 				if( rc != UNQLITE_OK ){
49341 					pEngine->pIo->xPageUnref(pOvfl);
49342 					return UNQLITE_ABORT;
49343 				}
49344 				nData = 0;
49345 			}else{
49346 				rc = xConsumer((const void *)zPayload,nByte,pUserData);
49347 				if( rc != UNQLITE_OK ){
49348 					pEngine->pIo->xPageUnref(pOvfl);
49349 					return UNQLITE_ABORT;
49350 				}
49351 				nData -= nByte;
49352 			}
49353 			/* Next overflow page in the chain */
49354 			SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
49355 			/* Unref the page */
49356 			pEngine->pIo->xPageUnref(pOvfl);
49357 		}
49358 		rc = UNQLITE_OK;
49359 	}
49360 	return rc;
49361 }
49362 /*
49363  * Given a cell, Consume its data by invoking the given callback for each extracted chunk.
49364  */
lhConsumeCellData(lhcell * pCell,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)49365 static int lhConsumeCellData(
49366 	lhcell *pCell, /* Target cell */
49367 	int (*xConsumer)(const void *,unsigned int,void *), /* Data consumer callback */
49368 	void *pUserData /* Last argument to xConsumer() */
49369 	)
49370 {
49371 	lhpage *pPage = pCell->pPage;
49372 	const unsigned char *zRaw = pPage->pRaw->zData;
49373 	const unsigned char *zPayload;
49374 	int rc;
49375 	/* Point to the payload area */
49376 	zPayload = &zRaw[pCell->iStart];
49377 	if( pCell->iOvfl == 0 ){
49378 		/* Best scenario, consume the data directly without any overflow page */
49379 		zPayload += L_HASH_CELL_SZ + pCell->nKey;
49380 		rc = xConsumer((const void *)zPayload,(sxu32)pCell->nData,pUserData);
49381 		if( rc != UNQLITE_OK ){
49382 			rc = UNQLITE_ABORT;
49383 		}
49384 	}else{
49385 		lhash_kv_engine *pEngine = pPage->pHash;
49386 		sxu64 nData = pCell->nData;
49387 		unqlite_page *pOvfl;
49388 		int fix_offset = 0;
49389 		sxu32 nByte;
49390 		pgno iOvfl;
49391 		/* Overflow page where data is stored */
49392 		iOvfl = pCell->iDataPage;
49393 		for(;;){
49394 			if( iOvfl == 0 || nData < 1 ){
49395 				/* no more overflow page */
49396 				break;
49397 			}
49398 			/* Point to the overflow page */
49399 			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
49400 			if( rc != UNQLITE_OK ){
49401 				return rc;
49402 			}
49403 			/* Point to the raw content */
49404 			zPayload = pOvfl->zData;
49405 			if( !fix_offset ){
49406 				/* Point to the data */
49407 				zPayload += pCell->iDataOfft;
49408 				nByte = pEngine->iPageSize - pCell->iDataOfft;
49409 				fix_offset = 1;
49410 			}else{
49411 				zPayload += 8;
49412 				/* Total usable bytes in an overflow page */
49413 				nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
49414 			}
49415 			/* Consume the data */
49416 			if( nData <= (sxu64)nByte ){
49417 				rc = xConsumer((const void *)zPayload,(unsigned int)nData,pUserData);
49418 				if( rc != UNQLITE_OK ){
49419 					pEngine->pIo->xPageUnref(pOvfl);
49420 					return UNQLITE_ABORT;
49421 				}
49422 				nData = 0;
49423 			}else{
49424 				if( nByte > 0 ){
49425 					rc = xConsumer((const void *)zPayload,nByte,pUserData);
49426 					if( rc != UNQLITE_OK ){
49427 						pEngine->pIo->xPageUnref(pOvfl);
49428 						return UNQLITE_ABORT;
49429 					}
49430 					nData -= nByte;
49431 				}
49432 			}
49433 			/* Next overflow page in the chain */
49434 			SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
49435 			/* Unref the page */
49436 			pEngine->pIo->xPageUnref(pOvfl);
49437 		}
49438 		rc = UNQLITE_OK;
49439 	}
49440 	return rc;
49441 }
49442 /*
49443  * Read the linear hash header (Page one of the database).
49444  */
lhash_read_header(lhash_kv_engine * pEngine,unqlite_page * pHeader)49445 static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
49446 {
49447 	const unsigned char *zRaw = pHeader->zData;
49448 	lhash_bmap_page *pMap;
49449 	sxu32 nHash;
49450 	int rc;
49451 	pEngine->pHeader = pHeader;
49452 	/* 4 byte magic number */
49453 	SyBigEndianUnpack32(zRaw,&pEngine->nMagic);
49454 	zRaw += 4;
49455 	if( pEngine->nMagic != L_HASH_MAGIC ){
49456 		/* Corrupt implementation */
49457 		return UNQLITE_CORRUPT;
49458 	}
49459 	/* 4 byte hash value to identify a valid hash function */
49460 	SyBigEndianUnpack32(zRaw,&nHash);
49461 	zRaw += 4;
49462 	/* Sanity check */
49463 	if( pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1) != nHash ){
49464 		/* Different hash function */
49465 		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Invalid hash function");
49466 		return UNQLITE_INVALID;
49467 	}
49468 	/* List of free pages */
49469 	SyBigEndianUnpack64(zRaw,&pEngine->nFreeList);
49470 	zRaw += 8;
49471 	/* Current split bucket */
49472 	SyBigEndianUnpack64(zRaw,&pEngine->split_bucket);
49473 	zRaw += 8;
49474 	/* Maximum split bucket */
49475 	SyBigEndianUnpack64(zRaw,&pEngine->max_split_bucket);
49476 	zRaw += 8;
49477 	/* Next generation */
49478 	pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1;
49479 	/* Initialiaze the bucket map */
49480 	pMap = &pEngine->sPageMap;
49481 	/* Fill in the structure */
49482 	pMap->iNum = pHeader->pgno;
49483 	/* Next page in the bucket map */
49484 	SyBigEndianUnpack64(zRaw,&pMap->iNext);
49485 	zRaw += 8;
49486 	/* Total number of records in the bucket map (This page only) */
49487 	SyBigEndianUnpack32(zRaw,&pMap->nRec);
49488 	zRaw += 4;
49489 	pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
49490 	/* Load the map in memory */
49491 	rc = lhMapLoadPage(pEngine,pMap,pHeader->zData);
49492 	if( rc != UNQLITE_OK ){
49493 		return rc;
49494 	}
49495 	/* Load the bucket map chain if any */
49496 	for(;;){
49497 		pgno iNext = pMap->iNext;
49498 		unqlite_page *pPage;
49499 		if( iNext == 0 ){
49500 			/* No more map pages */
49501 			break;
49502 		}
49503 		/* Point to the target page */
49504 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pPage);
49505 		if( rc != UNQLITE_OK ){
49506 			return rc;
49507 		}
49508 		/* Fill in the structure */
49509 		pMap->iNum = iNext;
49510 		pMap->iPtr = 0;
49511 		/* Load the map in memory */
49512 		rc = lhMapLoadPage(pEngine,pMap,pPage->zData);
49513 		if( rc != UNQLITE_OK ){
49514 			return rc;
49515 		}
49516 	}
49517 	/* All done */
49518 	return UNQLITE_OK;
49519 }
49520 /*
49521  * Perform a record lookup.
49522  */
lhRecordLookup(lhash_kv_engine * pEngine,const void * pKey,sxu32 nByte,lhcell ** ppCell)49523 static int lhRecordLookup(
49524 	lhash_kv_engine *pEngine, /* KV storage engine */
49525 	const void *pKey,         /* Lookup key */
49526 	sxu32 nByte,              /* Key length */
49527 	lhcell **ppCell           /* OUT: Target cell on success */
49528 	)
49529 {
49530 	lhash_bmap_rec *pRec;
49531 	lhpage *pPage;
49532 	lhcell *pCell;
49533 	pgno iBucket;
49534 	sxu32 nHash;
49535 	int rc;
49536 	/* Acquire the first page (hash Header) so that everything gets loaded autmatically */
49537 	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
49538 	if( rc != UNQLITE_OK ){
49539 		return rc;
49540 	}
49541 	/* Compute the hash of the key first */
49542 	nHash = pEngine->xHash(pKey,nByte);
49543 	/* Extract the logical (i.e. not real) page number */
49544 	iBucket = nHash & (pEngine->nmax_split_nucket - 1);
49545 	if( iBucket >= (pEngine->split_bucket + pEngine->max_split_bucket) ){
49546 		/* Low mask */
49547 		iBucket = nHash & (pEngine->max_split_bucket - 1);
49548 	}
49549 	/* Map the logical bucket number to real page number */
49550 	pRec = lhMapFindBucket(pEngine,iBucket);
49551 	if( pRec == 0 ){
49552 		/* No such entry */
49553 		return UNQLITE_NOTFOUND;
49554 	}
49555 	/* Load the master page and it's slave page in-memory  */
49556 	rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
49557 	if( rc != UNQLITE_OK ){
49558 		/* IO error, unlikely scenario */
49559 		return rc;
49560 	}
49561 	/* Lookup for the cell */
49562 	pCell = lhFindCell(pPage,pKey,nByte,nHash);
49563 	if( pCell == 0 ){
49564 		/* No such entry */
49565 		return UNQLITE_NOTFOUND;
49566 	}
49567 	if( ppCell ){
49568 		*ppCell = pCell;
49569 	}
49570 	return UNQLITE_OK;
49571 }
49572 /*
49573  * Acquire a new page either from the free list or ask the pager
49574  * for a new one.
49575  */
lhAcquirePage(lhash_kv_engine * pEngine,unqlite_page ** ppOut)49576 static int lhAcquirePage(lhash_kv_engine *pEngine,unqlite_page **ppOut)
49577 {
49578 	unqlite_page *pPage;
49579 	int rc;
49580 	if( pEngine->nFreeList != 0 ){
49581 		/* Acquire one from the free list */
49582 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pEngine->nFreeList,&pPage);
49583 		if( rc == UNQLITE_OK ){
49584 			/* Point to the next free page */
49585 			SyBigEndianUnpack64(pPage->zData,&pEngine->nFreeList);
49586 			/* Update the database header */
49587 			rc = pEngine->pIo->xWrite(pEngine->pHeader);
49588 			if( rc != UNQLITE_OK ){
49589 				return rc;
49590 			}
49591 			SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
49592 			/* Tell the pager do not journal this page */
49593 			pEngine->pIo->xDontJournal(pPage);
49594 			/* Return to the caller */
49595 			*ppOut = pPage;
49596 			/* All done */
49597 			return UNQLITE_OK;
49598 		}
49599 	}
49600 	/* Acquire a new page */
49601 	rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pPage);
49602 	if( rc != UNQLITE_OK ){
49603 		return rc;
49604 	}
49605 	/* Point to the target page */
49606 	*ppOut = pPage;
49607 	return UNQLITE_OK;
49608 }
49609 /*
49610  * Write a bucket map record to disk.
49611  */
lhMapWriteRecord(lhash_kv_engine * pEngine,pgno iLogic,pgno iReal)49612 static int lhMapWriteRecord(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
49613 {
49614 	lhash_bmap_page *pMap = &pEngine->sPageMap;
49615 	unqlite_page *pPage = 0;
49616 	int rc;
49617 	if( pMap->iPtr > (pEngine->iPageSize - 16) /* 8 byte logical bucket number + 8 byte real bucket number */ ){
49618 		unqlite_page *pOld;
49619 		/* Point to the old page */
49620 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pOld);
49621 		if( rc != UNQLITE_OK ){
49622 			return rc;
49623 		}
49624 		/* Acquire a new page */
49625 		rc = lhAcquirePage(pEngine,&pPage);
49626 		if( rc != UNQLITE_OK ){
49627 			return rc;
49628 		}
49629 		/* Reflect the change  */
49630 		pMap->iNext = 0;
49631 		pMap->iNum = pPage->pgno;
49632 		pMap->nRec = 0;
49633 		pMap->iPtr = 8/* Next page number */+4/* Total records in the map*/;
49634 		/* Link this page */
49635 		rc = pEngine->pIo->xWrite(pOld);
49636 		if( rc != UNQLITE_OK ){
49637 			return rc;
49638 		}
49639 		if( pOld->pgno == pEngine->pHeader->pgno ){
49640 			/* First page (Hash header) */
49641 			SyBigEndianPack64(&pOld->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/],pPage->pgno);
49642 		}else{
49643 			/* Link the new page */
49644 			SyBigEndianPack64(pOld->zData,pPage->pgno);
49645 			/* Unref */
49646 			pEngine->pIo->xPageUnref(pOld);
49647 		}
49648 		/* Assume the last bucket map page */
49649 		rc = pEngine->pIo->xWrite(pPage);
49650 		if( rc != UNQLITE_OK ){
49651 			return rc;
49652 		}
49653 		SyBigEndianPack64(pPage->zData,0); /* Next bucket map page on the list */
49654 	}
49655 	if( pPage == 0){
49656 		/* Point to the current map page */
49657 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pPage);
49658 		if( rc != UNQLITE_OK ){
49659 			return rc;
49660 		}
49661 	}
49662 	/* Make page writable */
49663 	rc = pEngine->pIo->xWrite(pPage);
49664 	if( rc != UNQLITE_OK ){
49665 		return rc;
49666 	}
49667 	/* Write the data */
49668 	SyBigEndianPack64(&pPage->zData[pMap->iPtr],iLogic);
49669 	pMap->iPtr += 8;
49670 	SyBigEndianPack64(&pPage->zData[pMap->iPtr],iReal);
49671 	pMap->iPtr += 8;
49672 	/* Install the bucket map */
49673 	rc = lhMapInstallBucket(pEngine,iLogic,iReal);
49674 	if( rc == UNQLITE_OK ){
49675 		/* Total number of records */
49676 		pMap->nRec++;
49677 		if( pPage->pgno == pEngine->pHeader->pgno ){
49678 			/* Page one: Always writable */
49679 			SyBigEndianPack32(
49680 				&pPage->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/+8/*Next map page*/],
49681 				pMap->nRec);
49682 		}else{
49683 			/* Make page writable */
49684 			rc = pEngine->pIo->xWrite(pPage);
49685 			if( rc != UNQLITE_OK ){
49686 				return rc;
49687 			}
49688 			SyBigEndianPack32(&pPage->zData[8],pMap->nRec);
49689 		}
49690 	}
49691 	return rc;
49692 }
49693 /*
49694  * Defragment a page.
49695  */
lhPageDefragment(lhpage * pPage)49696 static int lhPageDefragment(lhpage *pPage)
49697 {
49698 	lhash_kv_engine *pEngine = pPage->pHash;
49699 	unsigned char *zTmp,*zPtr,*zEnd,*zPayload;
49700 	lhcell *pCell;
49701 	/* Get a temporary page from the pager. This opertaion never fail */
49702 	zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle);
49703 	/* Move the target cells to the begining */
49704 	pCell = pPage->pList;
49705 	/* Write the slave page number */
49706 	SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave);
49707 	zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */
49708 	zEnd = &zTmp[pEngine->iPageSize];
49709 	pPage->sHdr.iOfft = 0; /* Offset of the first cell */
49710 	for(;;){
49711 		if( pCell == 0 ){
49712 			/* No more cells */
49713 			break;
49714 		}
49715 		if( pCell->pPage->pRaw->pgno == pPage->pRaw->pgno ){
49716 			/* Cell payload if locally stored */
49717 			zPayload = 0;
49718 			if( pCell->iOvfl == 0 ){
49719 				zPayload = &pCell->pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ];
49720 			}
49721 			/* Move the cell */
49722 			pCell->iNext = pPage->sHdr.iOfft;
49723 			pCell->iStart = (sxu16)(zPtr - zTmp); /* Offset where this cell start */
49724 			pPage->sHdr.iOfft = pCell->iStart;
49725 			/* Write the cell header */
49726 			/* 4 byte hash number */
49727 			SyBigEndianPack32(zPtr,pCell->nHash);
49728 			zPtr += 4;
49729 			/* 4 byte ley length */
49730 			SyBigEndianPack32(zPtr,pCell->nKey);
49731 			zPtr += 4;
49732 			/* 8 byte data length */
49733 			SyBigEndianPack64(zPtr,pCell->nData);
49734 			zPtr += 8;
49735 			/* 2 byte offset of the next cell */
49736 			SyBigEndianPack16(zPtr,pCell->iNext);
49737 			zPtr += 2;
49738 			/* 8 byte overflow page number */
49739 			SyBigEndianPack64(zPtr,pCell->iOvfl);
49740 			zPtr += 8;
49741 			if( zPayload ){
49742 				/* Local payload */
49743 				SyMemcpy((const void *)zPayload,zPtr,(sxu32)(pCell->nKey + pCell->nData));
49744 				zPtr += pCell->nKey + pCell->nData;
49745 			}
49746 			if( zPtr >= zEnd ){
49747 				/* Can't happen */
49748 				break;
49749 			}
49750 		}
49751 		/* Point to the next page */
49752 		pCell = pCell->pNext;
49753 	}
49754 	/* Mark the free block */
49755 	pPage->nFree = (sxu16)(zEnd - zPtr); /* Block length */
49756 	if( pPage->nFree > 3 ){
49757 		pPage->sHdr.iFree = (sxu16)(zPtr - zTmp); /* Offset of the free block */
49758 		/* Mark the block */
49759 		SyBigEndianPack16(zPtr,0); /* Offset of the next free block */
49760 		SyBigEndianPack16(&zPtr[2],pPage->nFree); /* Block length */
49761 	}else{
49762 		/* Block of length less than 4 bytes are simply discarded */
49763 		pPage->nFree = 0;
49764 		pPage->sHdr.iFree = 0;
49765 	}
49766 	/* Reflect the change */
49767 	SyBigEndianPack16(zTmp,pPage->sHdr.iOfft);     /* Offset of the first cell */
49768 	SyBigEndianPack16(&zTmp[2],pPage->sHdr.iFree); /* Offset of the first free block */
49769 	SyMemcpy((const void *)zTmp,pPage->pRaw->zData,pEngine->iPageSize);
49770 	/* All done */
49771 	return UNQLITE_OK;
49772 }
49773 /*
49774 ** Allocate nByte bytes of space on a page.
49775 **
49776 ** Return the index into pPage->pRaw->zData[] of the first byte of
49777 ** the new allocation. Or return 0 if there is not enough free
49778 ** space on the page to satisfy the allocation request.
49779 **
49780 ** If the page contains nBytes of free space but does not contain
49781 ** nBytes of contiguous free space, then this routine automatically
49782 ** calls defragementPage() to consolidate all free space before
49783 ** allocating the new chunk.
49784 */
lhAllocateSpace(lhpage * pPage,sxu64 nAmount,sxu16 * pOfft)49785 static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft)
49786 {
49787 	const unsigned char *zEnd,*zPtr;
49788 	sxu16 iNext,iBlksz,nByte;
49789 	unsigned char *zPrev;
49790 	int rc;
49791 	if( (sxu64)pPage->nFree < nAmount ){
49792 		/* Don't bother looking for a free chunk */
49793 		return UNQLITE_FULL;
49794 	}
49795 	if( pPage->nCell < 10 && ((int)nAmount >= (pPage->pHash->iPageSize / 2)) ){
49796 		/* Big chunk need an overflow page for its data */
49797 		return UNQLITE_FULL;
49798 	}
49799 	zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
49800 	zEnd = &pPage->pRaw->zData[pPage->pHash->iPageSize];
49801 	nByte = (sxu16)nAmount;
49802 	zPrev = 0;
49803 	iBlksz = 0; /* cc warning */
49804 	/* Perform the lookup */
49805 	for(;;){
49806 		if( zPtr >= zEnd ){
49807 			return UNQLITE_FULL;
49808 		}
49809 		/* Offset of the next free block */
49810 		SyBigEndianUnpack16(zPtr,&iNext);
49811 		/* Block size */
49812 		SyBigEndianUnpack16(&zPtr[2],&iBlksz);
49813 		if( iBlksz >= nByte ){
49814 			/* Got one */
49815 			break;
49816 		}
49817 		zPrev = (unsigned char *)zPtr;
49818 		if( iNext == 0 ){
49819 			/* No more free blocks, defragment the page */
49820 			rc = lhPageDefragment(pPage);
49821 			if( rc == UNQLITE_OK && pPage->nFree >= nByte) {
49822 				/* Free blocks are merged together */
49823 				iNext = 0;
49824 				zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
49825 				iBlksz = pPage->nFree;
49826 				zPrev = 0;
49827 				break;
49828 			}else{
49829 				return UNQLITE_FULL;
49830 			}
49831 		}
49832 		/* Point to the next free block */
49833 		zPtr = &pPage->pRaw->zData[iNext];
49834 	}
49835 	/* Acquire writer lock on this page */
49836 	rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
49837 	if( rc != UNQLITE_OK ){
49838 		return rc;
49839 	}
49840 	/* Save block offset */
49841 	*pOfft = (sxu16)(zPtr - pPage->pRaw->zData);
49842 	/* Fix pointers */
49843 	if( iBlksz >= nByte && (iBlksz - nByte) > 3 ){
49844 		unsigned char *zBlock = &pPage->pRaw->zData[(*pOfft) + nByte];
49845 		/* Create a new block */
49846 		zPtr = zBlock;
49847 		SyBigEndianPack16(zBlock,iNext); /* Offset of the next block */
49848 		SyBigEndianPack16(&zBlock[2],iBlksz-nByte); /* Block size*/
49849 		/* Offset of the new block */
49850 		iNext = (sxu16)(zPtr - pPage->pRaw->zData);
49851 		iBlksz = nByte;
49852 	}
49853 	/* Fix offsets */
49854 	if( zPrev ){
49855 		SyBigEndianPack16(zPrev,iNext);
49856 	}else{
49857 		/* First block */
49858 		pPage->sHdr.iFree = iNext;
49859 		/* Reflect on the page header */
49860 		SyBigEndianPack16(&pPage->pRaw->zData[2/* Offset of the first cell1*/],iNext);
49861 	}
49862 	/* All done */
49863 	pPage->nFree -= iBlksz;
49864 	return UNQLITE_OK;
49865 }
49866 /*
49867  * Write the cell header into the corresponding offset.
49868  */
lhCellWriteHeader(lhcell * pCell)49869 static int lhCellWriteHeader(lhcell *pCell)
49870 {
49871 	lhpage *pPage = pCell->pPage;
49872 	unsigned char *zRaw = pPage->pRaw->zData;
49873 	/* Seek to the desired location */
49874 	zRaw += pCell->iStart;
49875 	/* 4 byte hash number */
49876 	SyBigEndianPack32(zRaw,pCell->nHash);
49877 	zRaw += 4;
49878 	/* 4 byte key length */
49879 	SyBigEndianPack32(zRaw,pCell->nKey);
49880 	zRaw += 4;
49881 	/* 8 byte data length */
49882 	SyBigEndianPack64(zRaw,pCell->nData);
49883 	zRaw += 8;
49884 	/* 2 byte offset of the next cell */
49885 	pCell->iNext = pPage->sHdr.iOfft;
49886 	SyBigEndianPack16(zRaw,pCell->iNext);
49887 	zRaw += 2;
49888 	/* 8 byte overflow page number */
49889 	SyBigEndianPack64(zRaw,pCell->iOvfl);
49890 	/* Update the page header */
49891 	pPage->sHdr.iOfft = pCell->iStart;
49892 	/* pEngine->pIo->xWrite() has been successfully called on this page */
49893 	SyBigEndianPack16(pPage->pRaw->zData,pCell->iStart);
49894 	/* All done */
49895 	return UNQLITE_OK;
49896 }
49897 /*
49898  * Write local payload.
49899  */
lhCellWriteLocalPayload(lhcell * pCell,const void * pKey,sxu32 nKeylen,const void * pData,unqlite_int64 nDatalen)49900 static int lhCellWriteLocalPayload(lhcell *pCell,
49901 	const void *pKey,sxu32 nKeylen,
49902 	const void *pData,unqlite_int64 nDatalen
49903 	)
49904 {
49905 	/* A writer lock have been acquired on this page */
49906 	lhpage *pPage = pCell->pPage;
49907 	unsigned char *zRaw = pPage->pRaw->zData;
49908 	/* Seek to the desired location */
49909 	zRaw += pCell->iStart + L_HASH_CELL_SZ;
49910 	/* Write the key */
49911 	SyMemcpy(pKey,(void *)zRaw,nKeylen);
49912 	zRaw += nKeylen;
49913 	if( nDatalen > 0 ){
49914 		/* Write the Data */
49915 		SyMemcpy(pData,(void *)zRaw,(sxu32)nDatalen);
49916 	}
49917 	return UNQLITE_OK;
49918 }
49919 /*
49920  * Allocate as much overflow page we need to store the cell payload.
49921  */
lhCellWriteOvflPayload(lhcell * pCell,const void * pKey,sxu32 nKeylen,...)49922 static int lhCellWriteOvflPayload(lhcell *pCell,const void *pKey,sxu32 nKeylen,...)
49923 {
49924 	lhpage *pPage = pCell->pPage;
49925 	lhash_kv_engine *pEngine = pPage->pHash;
49926 	unqlite_page *pOvfl,*pFirst,*pNew;
49927 	const unsigned char *zPtr,*zEnd;
49928 	unsigned char *zRaw,*zRawEnd;
49929 	sxu32 nAvail;
49930 	va_list ap;
49931 	int rc;
49932 	/* Acquire a new overflow page */
49933 	rc = lhAcquirePage(pEngine,&pOvfl);
49934 	if( rc != UNQLITE_OK ){
49935 		return rc;
49936 	}
49937 	/* Acquire a writer lock */
49938 	rc = pEngine->pIo->xWrite(pOvfl);
49939 	if( rc != UNQLITE_OK ){
49940 		return rc;
49941 	}
49942 	pFirst = pOvfl;
49943 	/* Link */
49944 	pCell->iOvfl = pOvfl->pgno;
49945 	/* Update the cell header */
49946 	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4/*Hash*/ + 4/*Key*/ + 8/*Data*/ + 2 /*Next cell*/],pCell->iOvfl);
49947 	/* Start the write process */
49948 	zPtr = (const unsigned char *)pKey;
49949 	zEnd = &zPtr[nKeylen];
49950 	SyBigEndianPack64(pOvfl->zData,0); /* Next overflow page on the chain */
49951 	zRaw = &pOvfl->zData[8/* Next ovfl page*/ + 8 /* Data page */ + 2 /* Data offset*/];
49952 	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
49953 	pNew = pOvfl;
49954 	/* Write the key */
49955 	for(;;){
49956 		if( zPtr >= zEnd ){
49957 			break;
49958 		}
49959 		if( zRaw >= zRawEnd ){
49960 			/* Acquire a new page */
49961 			rc = lhAcquirePage(pEngine,&pNew);
49962 			if( rc != UNQLITE_OK ){
49963 				return rc;
49964 			}
49965 			rc = pEngine->pIo->xWrite(pNew);
49966 			if( rc != UNQLITE_OK ){
49967 				return rc;
49968 			}
49969 			/* Link */
49970 			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
49971 			pEngine->pIo->xPageUnref(pOvfl);
49972 			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
49973 			pOvfl = pNew;
49974 			zRaw = &pNew->zData[8];
49975 			zRawEnd = &pNew->zData[pEngine->iPageSize];
49976 		}
49977 		nAvail = (sxu32)(zRawEnd-zRaw);
49978 		nKeylen = (sxu32)(zEnd-zPtr);
49979 		if( nKeylen > nAvail ){
49980 			nKeylen = nAvail;
49981 		}
49982 		SyMemcpy((const void *)zPtr,(void *)zRaw,nKeylen);
49983 		/* Synchronize pointers */
49984 		zPtr += nKeylen;
49985 		zRaw += nKeylen;
49986 	}
49987 	rc = UNQLITE_OK;
49988 	va_start(ap,nKeylen);
49989 	pCell->iDataPage = pNew->pgno;
49990 	pCell->iDataOfft = (sxu16)(zRaw-pNew->zData);
49991 	/* Write the data page and its offset */
49992 	SyBigEndianPack64(&pFirst->zData[8/*Next ovfl*/],pCell->iDataPage);
49993 	SyBigEndianPack16(&pFirst->zData[8/*Next ovfl*/+8/*Data page*/],pCell->iDataOfft);
49994 	/* Write data */
49995 	for(;;){
49996 		const void *pData;
49997 		sxu32 nDatalen;
49998 		sxu64 nData;
49999 		pData = va_arg(ap,const void *);
50000 		nData = va_arg(ap,sxu64);
50001 		if( pData == 0 ){
50002 			/* No more chunks */
50003 			break;
50004 		}
50005 		/* Write this chunk */
50006 		zPtr = (const unsigned char *)pData;
50007 		zEnd = &zPtr[nData];
50008 		for(;;){
50009 			if( zPtr >= zEnd ){
50010 				break;
50011 			}
50012 			if( zRaw >= zRawEnd ){
50013 				/* Acquire a new page */
50014 				rc = lhAcquirePage(pEngine,&pNew);
50015 				if( rc != UNQLITE_OK ){
50016 					va_end(ap);
50017 					return rc;
50018 				}
50019 				rc = pEngine->pIo->xWrite(pNew);
50020 				if( rc != UNQLITE_OK ){
50021 					va_end(ap);
50022 					return rc;
50023 				}
50024 				/* Link */
50025 				SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50026 				pEngine->pIo->xPageUnref(pOvfl);
50027 				SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50028 				pOvfl = pNew;
50029 				zRaw = &pNew->zData[8];
50030 				zRawEnd = &pNew->zData[pEngine->iPageSize];
50031 			}
50032 			nAvail = (sxu32)(zRawEnd-zRaw);
50033 			nDatalen = (sxu32)(zEnd-zPtr);
50034 			if( nDatalen > nAvail ){
50035 				nDatalen = nAvail;
50036 			}
50037 			SyMemcpy((const void *)zPtr,(void *)zRaw,nDatalen);
50038 			/* Synchronize pointers */
50039 			zPtr += nDatalen;
50040 			zRaw += nDatalen;
50041 		}
50042 	}
50043 	/* Unref the overflow page */
50044 	pEngine->pIo->xPageUnref(pOvfl);
50045 	va_end(ap);
50046 	return UNQLITE_OK;
50047 }
50048 /*
50049  * Restore a page to the free list.
50050  */
lhRestorePage(lhash_kv_engine * pEngine,unqlite_page * pPage)50051 static int lhRestorePage(lhash_kv_engine *pEngine,unqlite_page *pPage)
50052 {
50053 	int rc;
50054 	rc = pEngine->pIo->xWrite(pEngine->pHeader);
50055 	if( rc != UNQLITE_OK ){
50056 		return rc;
50057 	}
50058 	rc = pEngine->pIo->xWrite(pPage);
50059 	if( rc != UNQLITE_OK ){
50060 		return rc;
50061 	}
50062 	/* Link to the list of free page */
50063 	SyBigEndianPack64(pPage->zData,pEngine->nFreeList);
50064 	pEngine->nFreeList = pPage->pgno;
50065 	SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
50066 	/* All done */
50067 	return UNQLITE_OK;
50068 }
50069 /*
50070  * Restore cell space and mark it as a free block.
50071  */
lhRestoreSpace(lhpage * pPage,sxu16 iOfft,sxu16 nByte)50072 static int lhRestoreSpace(lhpage *pPage,sxu16 iOfft,sxu16 nByte)
50073 {
50074 	unsigned char *zRaw;
50075 	if( nByte < 4 ){
50076 		/* At least 4 bytes of freespace must form a valid block */
50077 		return UNQLITE_OK;
50078 	}
50079 	/* pEngine->pIo->xWrite() has been successfully called on this page */
50080 	zRaw = &pPage->pRaw->zData[iOfft];
50081 	/* Mark as a free block */
50082 	SyBigEndianPack16(zRaw,pPage->sHdr.iFree); /* Offset of the next free block */
50083 	zRaw += 2;
50084 	SyBigEndianPack16(zRaw,nByte);
50085 	/* Link */
50086 	SyBigEndianPack16(&pPage->pRaw->zData[2/* offset of the first cell */],iOfft);
50087 	pPage->sHdr.iFree = iOfft;
50088 	pPage->nFree += nByte;
50089 	return UNQLITE_OK;
50090 }
50091 /* Forward declaration */
50092 static lhcell * lhFindSibeling(lhcell *pCell);
50093 /*
50094  * Unlink a cell.
50095  */
lhUnlinkCell(lhcell * pCell)50096 static int lhUnlinkCell(lhcell *pCell)
50097 {
50098 	lhash_kv_engine *pEngine = pCell->pPage->pHash;
50099 	lhpage *pPage = pCell->pPage;
50100 	sxu16 nByte = L_HASH_CELL_SZ;
50101 	lhcell *pPrev;
50102 	int rc;
50103 	rc = pEngine->pIo->xWrite(pPage->pRaw);
50104 	if( rc != UNQLITE_OK ){
50105 		return rc;
50106 	}
50107 	/* Bring the link */
50108 	pPrev = lhFindSibeling(pCell);
50109 	if( pPrev ){
50110 		pPrev->iNext = pCell->iNext;
50111 		/* Fix offsets in the page header */
50112 		SyBigEndianPack16(&pPage->pRaw->zData[pPrev->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
50113 	}else{
50114 		/* First entry on this page (either master or slave) */
50115 		pPage->sHdr.iOfft = pCell->iNext;
50116 		/* Update the page header */
50117 		SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
50118 	}
50119 	/* Restore cell space */
50120 	if( pCell->iOvfl == 0 ){
50121 		nByte += (sxu16)(pCell->nData + pCell->nKey);
50122 	}
50123 	lhRestoreSpace(pPage,pCell->iStart,nByte);
50124 	/* Discard the cell from the in-memory hashtable */
50125 	lhCellDiscard(pCell);
50126 	return UNQLITE_OK;
50127 }
50128 /*
50129  * Remove a cell and its paylod (key + data).
50130  */
lhRecordRemove(lhcell * pCell)50131 static int lhRecordRemove(lhcell *pCell)
50132 {
50133 	lhash_kv_engine *pEngine = pCell->pPage->pHash;
50134 	int rc;
50135 	if( pCell->iOvfl > 0){
50136 		/* Discard overflow pages */
50137 		unqlite_page *pOvfl;
50138 		pgno iNext = pCell->iOvfl;
50139 		for(;;){
50140 			/* Point to the overflow page */
50141 			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pOvfl);
50142 			if( rc != UNQLITE_OK ){
50143 				return rc;
50144 			}
50145 			/* Next page on the chain */
50146 			SyBigEndianUnpack64(pOvfl->zData,&iNext);
50147 			/* Restore the page to the free list */
50148 			rc = lhRestorePage(pEngine,pOvfl);
50149 			if( rc != UNQLITE_OK ){
50150 				return rc;
50151 			}
50152 			/* Unref */
50153 			pEngine->pIo->xPageUnref(pOvfl);
50154 			if( iNext == 0 ){
50155 				break;
50156 			}
50157 		}
50158 	}
50159 	/* Unlink the cell */
50160 	rc = lhUnlinkCell(pCell);
50161 	return rc;
50162 }
50163 /*
50164  * Find cell sibeling.
50165  */
lhFindSibeling(lhcell * pCell)50166 static lhcell * lhFindSibeling(lhcell *pCell)
50167 {
50168 	lhpage *pPage = pCell->pPage->pMaster;
50169 	lhcell *pEntry;
50170 	pEntry = pPage->pFirst;
50171 	while( pEntry ){
50172 		if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){
50173 			/* Sibeling found */
50174 			return pEntry;
50175 		}
50176 		/* Point to the previous entry */
50177 		pEntry = pEntry->pPrev;
50178 	}
50179 	/* Last inserted cell */
50180 	return 0;
50181 }
50182 /*
50183  * Move a cell to a new location with its new data.
50184  */
lhMoveLocalCell(lhcell * pCell,sxu16 iOfft,const void * pData,unqlite_int64 nData)50185 static int lhMoveLocalCell(
50186 	lhcell *pCell,
50187 	sxu16 iOfft,
50188 	const void *pData,
50189 	unqlite_int64 nData
50190 	)
50191 {
50192 	sxu16 iKeyOfft = pCell->iStart + L_HASH_CELL_SZ;
50193 	lhpage *pPage = pCell->pPage;
50194 	lhcell *pSibeling;
50195 	pSibeling = lhFindSibeling(pCell);
50196 	if( pSibeling ){
50197 		/* Fix link */
50198 		SyBigEndianPack16(&pPage->pRaw->zData[pSibeling->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
50199 		pSibeling->iNext = pCell->iNext;
50200 	}else{
50201 		/* First cell, update page header only */
50202 		SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
50203 		pPage->sHdr.iOfft = pCell->iNext;
50204 	}
50205 	/* Set the new offset */
50206 	pCell->iStart = iOfft;
50207 	pCell->nData = (sxu64)nData;
50208 	/* Write the cell payload */
50209 	lhCellWriteLocalPayload(pCell,(const void *)&pPage->pRaw->zData[iKeyOfft],pCell->nKey,pData,nData);
50210 	/* Finally write the cell header */
50211 	lhCellWriteHeader(pCell);
50212 	/* All done */
50213 	return UNQLITE_OK;
50214 }
50215 /*
50216  * Overwrite an existing record.
50217  */
lhRecordOverwrite(lhcell * pCell,const void * pData,unqlite_int64 nByte)50218 static int lhRecordOverwrite(
50219 	lhcell *pCell,
50220 	const void *pData,unqlite_int64 nByte
50221 	)
50222 {
50223 	lhash_kv_engine *pEngine = pCell->pPage->pHash;
50224 	unsigned char *zRaw,*zRawEnd,*zPayload;
50225 	const unsigned char *zPtr,*zEnd;
50226 	unqlite_page *pOvfl,*pOld,*pNew;
50227 	lhpage *pPage = pCell->pPage;
50228 	sxu32 nAvail;
50229 	pgno iOvfl;
50230 	int rc;
50231 	/* Acquire a writer lock on this page */
50232 	rc = pEngine->pIo->xWrite(pPage->pRaw);
50233 	if( rc != UNQLITE_OK ){
50234 		return rc;
50235 	}
50236 	if( pCell->iOvfl == 0 ){
50237 		/* Local payload, try to deal with the free space issues */
50238 		zPayload = &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey];
50239 		if( pCell->nData == (sxu64)nByte ){
50240 			/* Best scenario, simply a memcpy operation */
50241 			SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
50242 		}else if( (sxu64)nByte < pCell->nData ){
50243 			/* Shorter data, not so ugly */
50244 			SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
50245 			/* Update the cell header */
50246 			SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],nByte);
50247 			/* Restore freespace */
50248 			lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ + pCell->nKey + nByte),(sxu16)(pCell->nData - nByte));
50249 			/* New data size */
50250 			pCell->nData = (sxu64)nByte;
50251 		}else{
50252 			sxu16 iOfft = 0; /* cc warning */
50253 			/* Check if another chunk is available for this cell */
50254 			rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + nByte,&iOfft);
50255 			if( rc != UNQLITE_OK ){
50256 				/* Transfer the payload to an overflow page */
50257 				rc = lhCellWriteOvflPayload(pCell,&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,pData,nByte,(const void *)0);
50258 				if( rc != UNQLITE_OK ){
50259 					return rc;
50260 				}
50261 				/* Update the cell header */
50262 				SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],(sxu64)nByte);
50263 				/* Restore freespace */
50264 				lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
50265 				/* New data size */
50266 				pCell->nData = (sxu64)nByte;
50267 			}else{
50268 				sxu16 iOldOfft = pCell->iStart;
50269 				sxu32 iOld = (sxu32)pCell->nData;
50270 				/* Space is available, transfer the cell */
50271 				lhMoveLocalCell(pCell,iOfft,pData,nByte);
50272 				/* Restore cell space */
50273 				lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
50274 			}
50275 		}
50276 		return UNQLITE_OK;
50277 	}
50278 	/* Point to the overflow page */
50279 	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
50280 	if( rc != UNQLITE_OK ){
50281 		return rc;
50282 	}
50283 	/* Relase all old overflow pages first */
50284 	SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
50285 	pOld = pOvfl;
50286 	for(;;){
50287 		if( iOvfl == 0 ){
50288 			/* No more overflow pages on the chain */
50289 			break;
50290 		}
50291 		/* Point to the target page */
50292 		if( UNQLITE_OK != pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOld) ){
50293 			/* Not so fatal if something goes wrong here */
50294 			break;
50295 		}
50296 		/* Next overflow page to be released */
50297 		SyBigEndianUnpack64(pOld->zData,&iOvfl);
50298 		if( pOld != pOvfl ){ /* xx: chm is maniac */
50299 			/* Restore the page to the free list */
50300 			lhRestorePage(pEngine,pOld);
50301 			/* Unref */
50302 			pEngine->pIo->xPageUnref(pOld);
50303 		}
50304 	}
50305 	/* Point to the data offset */
50306 	zRaw = &pOvfl->zData[pCell->iDataOfft];
50307 	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
50308 	/* The data to be stored */
50309 	zPtr = (const unsigned char *)pData;
50310 	zEnd = &zPtr[nByte];
50311 	/* Start the overwrite process */
50312 	/* Acquire a writer lock */
50313 	rc = pEngine->pIo->xWrite(pOvfl);
50314 	if( rc != UNQLITE_OK ){
50315 		return rc;
50316 	}
50317 	SyBigEndianPack64(pOvfl->zData,0);
50318 	for(;;){
50319 		sxu32 nLen;
50320 		if( zPtr >= zEnd ){
50321 			break;
50322 		}
50323 		if( zRaw >= zRawEnd ){
50324 			/* Acquire a new page */
50325 			rc = lhAcquirePage(pEngine,&pNew);
50326 			if( rc != UNQLITE_OK ){
50327 				return rc;
50328 			}
50329 			rc = pEngine->pIo->xWrite(pNew);
50330 			if( rc != UNQLITE_OK ){
50331 				return rc;
50332 			}
50333 			/* Link */
50334 			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50335 			pEngine->pIo->xPageUnref(pOvfl);
50336 			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50337 			pOvfl = pNew;
50338 			zRaw = &pNew->zData[8];
50339 			zRawEnd = &pNew->zData[pEngine->iPageSize];
50340 		}
50341 		nAvail = (sxu32)(zRawEnd-zRaw);
50342 		nLen = (sxu32)(zEnd-zPtr);
50343 		if( nLen > nAvail ){
50344 			nLen = nAvail;
50345 		}
50346 		SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
50347 		/* Synchronize pointers */
50348 		zPtr += nLen;
50349 		zRaw += nLen;
50350 	}
50351 	/* Unref the last overflow page */
50352 	pEngine->pIo->xPageUnref(pOvfl);
50353 	/* Finally, update the cell header */
50354 	pCell->nData = (sxu64)nByte;
50355 	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
50356 	/* All done */
50357 	return UNQLITE_OK;
50358 }
50359 /*
50360  * Append data to an existing record.
50361  */
lhRecordAppend(lhcell * pCell,const void * pData,unqlite_int64 nByte)50362 static int lhRecordAppend(
50363 	lhcell *pCell,
50364 	const void *pData,unqlite_int64 nByte
50365 	)
50366 {
50367 	lhash_kv_engine *pEngine = pCell->pPage->pHash;
50368 	const unsigned char *zPtr,*zEnd;
50369 	lhpage *pPage = pCell->pPage;
50370 	unsigned char *zRaw,*zRawEnd;
50371 	unqlite_page *pOvfl,*pNew;
50372 	sxu64 nDatalen;
50373 	sxu32 nAvail;
50374 	pgno iOvfl;
50375 	int rc;
50376 	if( pCell->nData + nByte < pCell->nData ){
50377 		/* Overflow */
50378 		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
50379 		return UNQLITE_LIMIT;
50380 	}
50381 	/* Acquire a writer lock on this page */
50382 	rc = pEngine->pIo->xWrite(pPage->pRaw);
50383 	if( rc != UNQLITE_OK ){
50384 		return rc;
50385 	}
50386 	if( pCell->iOvfl == 0 ){
50387 		sxu16 iOfft = 0; /* cc warning */
50388 		/* Local payload, check for a bigger place */
50389 		rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + pCell->nData + nByte,&iOfft);
50390 		if( rc != UNQLITE_OK ){
50391 			/* Transfer the payload to an overflow page */
50392 			rc = lhCellWriteOvflPayload(pCell,
50393 				&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,
50394 				(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],pCell->nData,
50395 				pData,nByte,
50396 				(const void *)0);
50397 			if( rc != UNQLITE_OK ){
50398 				return rc;
50399 			}
50400 			/* Update the cell header */
50401 			SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData + nByte);
50402 			/* Restore freespace */
50403 			lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
50404 			/* New data size */
50405 			pCell->nData += nByte;
50406 		}else{
50407 			sxu16 iOldOfft = pCell->iStart;
50408 			sxu32 iOld = (sxu32)pCell->nData;
50409 			SyBlob sWorker;
50410 			SyBlobInit(&sWorker,&pEngine->sAllocator);
50411 			/* Copy the old data */
50412 			rc = SyBlobAppend(&sWorker,(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],(sxu32)pCell->nData);
50413 			if( rc == SXRET_OK ){
50414 				/* Append the new data */
50415 				rc = SyBlobAppend(&sWorker,pData,(sxu32)nByte);
50416 			}
50417 			if( rc != UNQLITE_OK ){
50418 				SyBlobRelease(&sWorker);
50419 				return rc;
50420 			}
50421 			/* Space is available, transfer the cell */
50422 			lhMoveLocalCell(pCell,iOfft,SyBlobData(&sWorker),(unqlite_int64)SyBlobLength(&sWorker));
50423 			/* Restore cell space */
50424 			lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
50425 			/* All done */
50426 			SyBlobRelease(&sWorker);
50427 		}
50428 		return UNQLITE_OK;
50429 	}
50430 	/* Point to the overflow page which hold the data */
50431 	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
50432 	if( rc != UNQLITE_OK ){
50433 		return rc;
50434 	}
50435 	/* Next overflow page in the chain */
50436 	SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
50437 	/* Point to the end of the chunk */
50438 	zRaw = &pOvfl->zData[pCell->iDataOfft];
50439 	zRawEnd = &pOvfl->zData[pEngine->iPageSize];
50440 	nDatalen = pCell->nData;
50441 	nAvail = (sxu32)(zRawEnd - zRaw);
50442 	for(;;){
50443 		if( zRaw >= zRawEnd ){
50444 			if( iOvfl == 0 ){
50445 				/* Cant happen */
50446 				pEngine->pIo->xErr(pEngine->pIo->pHandle,"Corrupt overflow page");
50447 				return UNQLITE_CORRUPT;
50448 			}
50449 			rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pNew);
50450 			if( rc != UNQLITE_OK ){
50451 				return rc;
50452 			}
50453 			/* Next overflow page on the chain */
50454 			SyBigEndianUnpack64(pNew->zData,&iOvfl);
50455 			/* Unref the previous overflow page */
50456 			pEngine->pIo->xPageUnref(pOvfl);
50457 			/* Point to the new chunk */
50458 			zRaw = &pNew->zData[8];
50459 			zRawEnd = &pNew->zData[pCell->pPage->pHash->iPageSize];
50460 			nAvail = L_HASH_OVERFLOW_SIZE(pCell->pPage->pHash->iPageSize);
50461 			pOvfl = pNew;
50462 		}
50463 		if( (sxu64)nAvail > nDatalen ){
50464 			zRaw += nDatalen;
50465 			break;
50466 		}else{
50467 			nDatalen -= nAvail;
50468 		}
50469 		zRaw += nAvail;
50470 	}
50471 	/* Start the append process */
50472 	zPtr = (const unsigned char *)pData;
50473 	zEnd = &zPtr[nByte];
50474 	/* Acquire a writer lock */
50475 	rc = pEngine->pIo->xWrite(pOvfl);
50476 	if( rc != UNQLITE_OK ){
50477 		return rc;
50478 	}
50479 	for(;;){
50480 		sxu32 nLen;
50481 		if( zPtr >= zEnd ){
50482 			break;
50483 		}
50484 		if( zRaw >= zRawEnd ){
50485 			/* Acquire a new page */
50486 			rc = lhAcquirePage(pEngine,&pNew);
50487 			if( rc != UNQLITE_OK ){
50488 				return rc;
50489 			}
50490 			rc = pEngine->pIo->xWrite(pNew);
50491 			if( rc != UNQLITE_OK ){
50492 				return rc;
50493 			}
50494 			/* Link */
50495 			SyBigEndianPack64(pOvfl->zData,pNew->pgno);
50496 			pEngine->pIo->xPageUnref(pOvfl);
50497 			SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
50498 			pOvfl = pNew;
50499 			zRaw = &pNew->zData[8];
50500 			zRawEnd = &pNew->zData[pEngine->iPageSize];
50501 		}
50502 		nAvail = (sxu32)(zRawEnd-zRaw);
50503 		nLen = (sxu32)(zEnd-zPtr);
50504 		if( nLen > nAvail ){
50505 			nLen = nAvail;
50506 		}
50507 		SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
50508 		/* Synchronize pointers */
50509 		zPtr += nLen;
50510 		zRaw += nLen;
50511 	}
50512 	/* Unref the last overflow page */
50513 	pEngine->pIo->xPageUnref(pOvfl);
50514 	/* Finally, update the cell header */
50515 	pCell->nData += nByte;
50516 	SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
50517 	/* All done */
50518 	return UNQLITE_OK;
50519 }
50520 /*
50521  * A write privilege have been acquired on this page.
50522  * Mark it as an empty page (No cells).
50523  */
lhSetEmptyPage(lhpage * pPage)50524 static int lhSetEmptyPage(lhpage *pPage)
50525 {
50526 	unsigned char *zRaw = pPage->pRaw->zData;
50527 	lhphdr *pHeader = &pPage->sHdr;
50528 	sxu16 nByte;
50529 	int rc;
50530 	/* Acquire a writer lock */
50531 	rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
50532 	if( rc != UNQLITE_OK ){
50533 		return rc;
50534 	}
50535 	/* Offset of the first cell */
50536 	SyBigEndianPack16(zRaw,0);
50537 	zRaw += 2;
50538 	/* Offset of the first free block */
50539 	pHeader->iFree = L_HASH_PAGE_HDR_SZ;
50540 	SyBigEndianPack16(zRaw,L_HASH_PAGE_HDR_SZ);
50541 	zRaw += 2;
50542 	/* Slave page number */
50543 	SyBigEndianPack64(zRaw,0);
50544 	zRaw += 8;
50545 	/* Fill the free block */
50546 	SyBigEndianPack16(zRaw,0); /* Offset of the next free block */
50547 	zRaw += 2;
50548 	nByte = (sxu16)L_HASH_MX_FREE_SPACE(pPage->pHash->iPageSize);
50549 	SyBigEndianPack16(zRaw,nByte);
50550 	pPage->nFree = nByte;
50551 	/* Do not add this page to the hot dirty list */
50552 	pPage->pHash->pIo->xDontMkHot(pPage->pRaw);
50553 	return UNQLITE_OK;
50554 }
50555 /* Forward declaration */
50556 static int lhSlaveStore(
50557 	lhpage *pPage,
50558 	const void *pKey,sxu32 nKeyLen,
50559 	const void *pData,unqlite_int64 nDataLen,
50560 	sxu32 nHash
50561 	);
50562 /*
50563  * Store a cell and its payload in a given page.
50564  */
lhStoreCell(lhpage * pPage,const void * pKey,sxu32 nKeyLen,const void * pData,unqlite_int64 nDataLen,sxu32 nHash,int auto_append)50565 static int lhStoreCell(
50566 	lhpage *pPage, /* Target page */
50567 	const void *pKey,sxu32 nKeyLen, /* Payload: Key */
50568 	const void *pData,unqlite_int64 nDataLen, /* Payload: Data */
50569 	sxu32 nHash, /* Hash of the key */
50570 	int auto_append /* Auto append a slave page if full */
50571 	)
50572 {
50573 	lhash_kv_engine *pEngine = pPage->pHash;
50574 	int iNeedOvfl = 0; /* Need overflow page for this cell and its payload*/
50575 	lhcell *pCell;
50576 	sxu16 nOfft;
50577 	int rc;
50578 	/* Acquire a writer lock on this page first */
50579 	rc = pEngine->pIo->xWrite(pPage->pRaw);
50580 	if( rc != UNQLITE_OK ){
50581 		return rc;
50582 	}
50583 	/* Check for a free block  */
50584 	rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ+nKeyLen+nDataLen,&nOfft);
50585 	if( rc != UNQLITE_OK ){
50586 		/* Check for a free block to hold a single cell only (without payload) */
50587 		rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
50588 		if( rc != UNQLITE_OK ){
50589 			if( !auto_append ){
50590 				/* A split must be done */
50591 				return UNQLITE_FULL;
50592 			}else{
50593 				/* Store this record in a slave page */
50594 				rc = lhSlaveStore(pPage,pKey,nKeyLen,pData,nDataLen,nHash);
50595 				return rc;
50596 			}
50597 		}
50598 		iNeedOvfl = 1;
50599 	}
50600 	/* Allocate a new cell instance */
50601 	pCell = lhNewCell(pEngine,pPage);
50602 	if( pCell == 0 ){
50603 		pEngine->pIo->xErr(pEngine->pIo->pHandle,"KV store is running out of memory");
50604 		return UNQLITE_NOMEM;
50605 	}
50606 	/* Fill-in the structure */
50607 	pCell->iStart = nOfft;
50608 	pCell->nKey = nKeyLen;
50609 	pCell->nData = (sxu64)nDataLen;
50610 	pCell->nHash = nHash;
50611 	if( nKeyLen < 262144 /* 256 KB */ ){
50612 		/* Keep the key in-memory for fast lookup */
50613 		SyBlobAppend(&pCell->sKey,pKey,nKeyLen);
50614 	}
50615 	/* Link the cell */
50616 	rc = lhInstallCell(pCell);
50617 	if( rc != UNQLITE_OK ){
50618 		return rc;
50619 	}
50620 	/* Write the payload */
50621 	if( iNeedOvfl ){
50622 		rc = lhCellWriteOvflPayload(pCell,pKey,nKeyLen,pData,nDataLen,(const void *)0);
50623 		if( rc != UNQLITE_OK ){
50624 			lhCellDiscard(pCell);
50625 			return rc;
50626 		}
50627 	}else{
50628 		lhCellWriteLocalPayload(pCell,pKey,nKeyLen,pData,nDataLen);
50629 	}
50630 	/* Finally, Write the cell header */
50631 	lhCellWriteHeader(pCell);
50632 	/* All done */
50633 	return UNQLITE_OK;
50634 }
50635 /*
50636  * Find a slave page capable of hosting the given amount.
50637  */
lhFindSlavePage(lhpage * pPage,sxu64 nAmount,sxu16 * pOfft,lhpage ** ppSlave)50638 static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppSlave)
50639 {
50640 	lhash_kv_engine *pEngine = pPage->pHash;
50641 	lhpage *pMaster = pPage->pMaster;
50642 	lhpage *pSlave = pMaster->pSlave;
50643 	unqlite_page *pRaw;
50644 	lhpage *pNew;
50645 	sxu16 iOfft;
50646 	sxi32 i;
50647 	int rc;
50648 	/* Look for an already attached slave page */
50649 	for( i = 0 ; i < pMaster->iSlave ; ++i ){
50650 		/* Find a free chunk big enough */
50651 		sxu16 size = L_HASH_CELL_SZ + nAmount;
50652 		rc = lhAllocateSpace(pSlave,size,&iOfft);
50653 		if( rc != UNQLITE_OK ){
50654 			/* A space for cell header only */
50655 			size = L_HASH_CELL_SZ;
50656 			rc = lhAllocateSpace(pSlave,size,&iOfft);
50657 		}
50658 		if( rc == UNQLITE_OK ){
50659 			/* All done */
50660 			if( pOfft ){
50661 				*pOfft = iOfft;
50662 			}else{
50663 				rc = lhRestoreSpace(pSlave, iOfft, size);
50664 			}
50665 			*ppSlave = pSlave;
50666 			return rc;
50667 		}
50668 		/* Point to the next slave page */
50669 		pSlave = pSlave->pNextSlave;
50670 	}
50671 	/* Acquire a new slave page */
50672 	rc = lhAcquirePage(pEngine,&pRaw);
50673 	if( rc != UNQLITE_OK ){
50674 		return rc;
50675 	}
50676 	/* Last slave page */
50677 	pSlave = pMaster->pSlave;
50678 	if( pSlave == 0 ){
50679 		/* First slave page */
50680 		pSlave = pMaster;
50681 	}
50682 	/* Initialize the page */
50683 	pNew = lhNewPage(pEngine,pRaw,pMaster);
50684 	if( pNew == 0 ){
50685 		return UNQLITE_NOMEM;
50686 	}
50687 	/* Mark as an empty page */
50688 	rc = lhSetEmptyPage(pNew);
50689 	if( rc != UNQLITE_OK ){
50690 		goto fail;
50691 	}
50692 	if( pOfft ){
50693 		/* Look for a free block */
50694 		if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){
50695 			/* Cell header only */
50696 			lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */
50697 		}
50698 		*pOfft = iOfft;
50699 	}
50700 	/* Link this page to the previous slave page */
50701 	rc = pEngine->pIo->xWrite(pSlave->pRaw);
50702 	if( rc != UNQLITE_OK ){
50703 		goto fail;
50704 	}
50705 	/* Reflect in the page header */
50706 	SyBigEndianPack64(&pSlave->pRaw->zData[2/*Cell offset*/+2/*Free block offset*/],pRaw->pgno);
50707 	pSlave->sHdr.iSlave = pRaw->pgno;
50708 	/* All done */
50709 	*ppSlave = pNew;
50710 	return UNQLITE_OK;
50711 fail:
50712 	pEngine->pIo->xPageUnref(pNew->pRaw); /* pNew will be released in this call */
50713 	return rc;
50714 
50715 }
50716 /*
50717  * Perform a store operation in a slave page.
50718  */
lhSlaveStore(lhpage * pPage,const void * pKey,sxu32 nKeyLen,const void * pData,unqlite_int64 nDataLen,sxu32 nHash)50719 static int lhSlaveStore(
50720 	lhpage *pPage, /* Master page */
50721 	const void *pKey,sxu32 nKeyLen, /* Payload: key */
50722 	const void *pData,unqlite_int64 nDataLen, /* Payload: data */
50723 	sxu32 nHash /* Hash of the key */
50724 	)
50725 {
50726 	lhpage *pSlave;
50727 	int rc;
50728 	/* Find a slave page */
50729 	rc = lhFindSlavePage(pPage,nKeyLen + nDataLen,0,&pSlave);
50730 	if( rc != UNQLITE_OK ){
50731 		return rc;
50732 	}
50733 	/* Perform the insertion in the slave page */
50734 	rc = lhStoreCell(pSlave,pKey,nKeyLen,pData,nDataLen,nHash,1);
50735 	return rc;
50736 }
50737 /*
50738  * Transfer a cell to a new page (either a master or slave).
50739  */
lhTransferCell(lhcell * pTarget,lhpage * pPage)50740 static int lhTransferCell(lhcell *pTarget,lhpage *pPage)
50741 {
50742 	lhcell *pCell;
50743 	sxu16 nOfft;
50744 	int rc;
50745 	/* Check for a free block to hold a single cell only */
50746 	rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
50747 	if( rc != UNQLITE_OK ){
50748 		/* Store in a slave page */
50749 		rc = lhFindSlavePage(pPage,L_HASH_CELL_SZ,&nOfft,&pPage);
50750 		if( rc != UNQLITE_OK ){
50751 			return rc;
50752 		}
50753 	}
50754 	/* Allocate a new cell instance */
50755 	pCell = lhNewCell(pPage->pHash,pPage);
50756 	if( pCell == 0 ){
50757 		return UNQLITE_NOMEM;
50758 	}
50759 	/* Fill-in the structure */
50760 	pCell->iStart = nOfft;
50761 	pCell->nData  = pTarget->nData;
50762 	pCell->nKey   = pTarget->nKey;
50763 	pCell->iOvfl  = pTarget->iOvfl;
50764 	pCell->iDataOfft = pTarget->iDataOfft;
50765 	pCell->iDataPage = pTarget->iDataPage;
50766 	pCell->nHash = pTarget->nHash;
50767 	SyBlobDup(&pTarget->sKey,&pCell->sKey);
50768 	/* Link the cell */
50769 	rc = lhInstallCell(pCell);
50770 	if( rc != UNQLITE_OK ){
50771 		return rc;
50772 	}
50773 	/* Finally, Write the cell header */
50774 	lhCellWriteHeader(pCell);
50775 	/* All done */
50776 	return UNQLITE_OK;
50777 }
50778 /*
50779  * Perform a page split.
50780  */
lhPageSplit(lhpage * pOld,lhpage * pNew,pgno split_bucket,pgno high_mask)50781 static int lhPageSplit(
50782 	lhpage *pOld,      /* Page to be split */
50783 	lhpage *pNew,      /* New page */
50784 	pgno split_bucket, /* Current split bucket */
50785 	pgno high_mask     /* High mask (Max split bucket - 1) */
50786 	)
50787 {
50788 	lhcell *pCell,*pNext;
50789 	SyBlob sWorker;
50790 	pgno iBucket;
50791 	int rc;
50792 	SyBlobInit(&sWorker,&pOld->pHash->sAllocator);
50793 	/* Perform the split */
50794 	pCell = pOld->pList;
50795 	for( ;; ){
50796 		if( pCell == 0 ){
50797 			/* No more cells */
50798 			break;
50799 		}
50800 		/* Obtain the new logical bucket */
50801 		iBucket = pCell->nHash & high_mask;
50802 		pNext =  pCell->pNext;
50803 		if( iBucket != split_bucket){
50804 			rc = UNQLITE_OK;
50805 			if( pCell->iOvfl ){
50806 				/* Transfer the cell only */
50807 				rc = lhTransferCell(pCell,pNew);
50808 			}else{
50809 				/* Transfer the cell and its payload */
50810 				SyBlobReset(&sWorker);
50811 				if( SyBlobLength(&pCell->sKey) < 1 ){
50812 					/* Consume the key */
50813 					rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,0);
50814 					if( rc != UNQLITE_OK ){
50815 						goto fail;
50816 					}
50817 				}
50818 				/* Consume the data (Very small data < 65k) */
50819 				rc = lhConsumeCellData(pCell,unqliteDataConsumer,&sWorker);
50820 				if( rc != UNQLITE_OK ){
50821 					goto fail;
50822 				}
50823 				/* Perform the transfer */
50824 				rc = lhStoreCell(
50825 					pNew,
50826 					SyBlobData(&pCell->sKey),(int)SyBlobLength(&pCell->sKey),
50827 					SyBlobData(&sWorker),SyBlobLength(&sWorker),
50828 					pCell->nHash,
50829 					1
50830 					);
50831 			}
50832 			if( rc != UNQLITE_OK ){
50833 				goto fail;
50834 			}
50835 			/* Discard the cell from the old page */
50836 			lhUnlinkCell(pCell);
50837 		}
50838 		/* Point to the next cell */
50839 		pCell = pNext;
50840 	}
50841 	/* All done */
50842 	rc = UNQLITE_OK;
50843 fail:
50844 	SyBlobRelease(&sWorker);
50845 	return rc;
50846 }
50847 /*
50848  * Perform the infamous linear hash split operation.
50849  */
lhSplit(lhpage * pTarget,int * pRetry)50850 static int lhSplit(lhpage *pTarget,int *pRetry)
50851 {
50852 	lhash_kv_engine *pEngine = pTarget->pHash;
50853 	lhash_bmap_rec *pRec;
50854 	lhpage *pOld,*pNew;
50855 	unqlite_page *pRaw;
50856 	int rc;
50857 	/* Get the real page number of the bucket to split */
50858 	pRec = lhMapFindBucket(pEngine,pEngine->split_bucket);
50859 	if( pRec == 0 ){
50860 		/* Can't happen */
50861 		return UNQLITE_CORRUPT;
50862 	}
50863 	/* Load the page to be split */
50864 	rc = lhLoadPage(pEngine,pRec->iReal,0,&pOld,0);
50865 	if( rc != UNQLITE_OK ){
50866 		return rc;
50867 	}
50868 	/* Request a new page */
50869 	rc = lhAcquirePage(pEngine,&pRaw);
50870 	if( rc != UNQLITE_OK ){
50871 		return rc;
50872 	}
50873 	/* Initialize the page */
50874 	pNew = lhNewPage(pEngine,pRaw,0);
50875 	if( pNew == 0 ){
50876 		return UNQLITE_NOMEM;
50877 	}
50878 	/* Mark as an empty page */
50879 	rc = lhSetEmptyPage(pNew);
50880 	if( rc != UNQLITE_OK ){
50881 		goto fail;
50882 	}
50883 	/* Install and write the logical map record */
50884 	rc = lhMapWriteRecord(pEngine,
50885 		pEngine->split_bucket + pEngine->max_split_bucket,
50886 		pRaw->pgno
50887 		);
50888 	if( rc != UNQLITE_OK ){
50889 		goto fail;
50890 	}
50891 	if( pTarget->pRaw->pgno == pOld->pRaw->pgno ){
50892 		*pRetry = 1;
50893 	}
50894 	/* Perform the split */
50895 	rc = lhPageSplit(pOld,pNew,pEngine->split_bucket,pEngine->nmax_split_nucket - 1);
50896 	if( rc != UNQLITE_OK ){
50897 		goto fail;
50898 	}
50899 	/* Update the database header */
50900 	pEngine->split_bucket++;
50901 	/* Acquire a writer lock on the first page */
50902 	rc = pEngine->pIo->xWrite(pEngine->pHeader);
50903 	if( rc != UNQLITE_OK ){
50904 		return rc;
50905 	}
50906 	if( pEngine->split_bucket >= pEngine->max_split_bucket ){
50907 		/* Increment the generation number */
50908 		pEngine->split_bucket = 0;
50909 		pEngine->max_split_bucket = pEngine->nmax_split_nucket;
50910 		pEngine->nmax_split_nucket <<= 1;
50911 		if( !pEngine->nmax_split_nucket ){
50912 			/* If this happen to your installation, please tell us <chm@symisc.net> */
50913 			pEngine->pIo->xErr(pEngine->pIo->pHandle,"Database page (64-bit integer) limit reached");
50914 			return UNQLITE_LIMIT;
50915 		}
50916 		/* Reflect in the page header */
50917 		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
50918 		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/+8/*Split bucket*/],pEngine->max_split_bucket);
50919 	}else{
50920 		/* Modify only the split bucket */
50921 		SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
50922 	}
50923 	/* All done */
50924 	return UNQLITE_OK;
50925 fail:
50926 	pEngine->pIo->xPageUnref(pNew->pRaw);
50927 	return rc;
50928 }
50929 /*
50930  * Store a record in the target page.
50931  */
lhRecordInstall(lhpage * pPage,sxu32 nHash,const void * pKey,sxu32 nKeyLen,const void * pData,unqlite_int64 nDataLen)50932 static int lhRecordInstall(
50933 	  lhpage *pPage, /* Target page */
50934 	  sxu32 nHash,   /* Hash of the key */
50935 	  const void *pKey,sxu32 nKeyLen,          /* Payload: Key */
50936 	  const void *pData,unqlite_int64 nDataLen /* Payload: Data */
50937 	  )
50938 {
50939 	int rc;
50940 	rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,0);
50941 	if( rc == UNQLITE_FULL ){
50942 		int do_retry = 0;
50943 		/* Split */
50944 		rc = lhSplit(pPage,&do_retry);
50945 		if( rc == UNQLITE_OK ){
50946 			if( do_retry ){
50947 				/* Re-calculate logical bucket number */
50948 				return SXERR_RETRY;
50949 			}
50950 			/* Perform the store */
50951 			rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
50952 		}
50953 	}
50954 	return rc;
50955 }
50956 /*
50957  * Insert a record (Either overwrite or append operation) in our database.
50958  */
lh_record_insert(unqlite_kv_engine * pKv,const void * pKey,sxu32 nKeyLen,const void * pData,unqlite_int64 nDataLen,int is_append)50959 static int lh_record_insert(
50960 	  unqlite_kv_engine *pKv,         /* KV store */
50961 	  const void *pKey,sxu32 nKeyLen, /* Payload: Key */
50962 	  const void *pData,unqlite_int64 nDataLen, /* Payload: data */
50963 	  int is_append /* True for an append operation */
50964 	  )
50965 {
50966 	lhash_kv_engine *pEngine = (lhash_kv_engine *)pKv;
50967 	lhash_bmap_rec *pRec;
50968 	unqlite_page *pRaw;
50969 	lhpage *pPage;
50970 	lhcell *pCell;
50971 	pgno iBucket;
50972 	sxu32 nHash;
50973 	int iCnt;
50974 	int rc;
50975 
50976 	/* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */
50977 	rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
50978 	if( rc != UNQLITE_OK ){
50979 		return rc;
50980 	}
50981 	iCnt = 0;
50982 	/* Compute the hash of the key first */
50983 	nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
50984 retry:
50985 	/* Extract the logical bucket number */
50986 	iBucket = nHash & (pEngine->nmax_split_nucket - 1);
50987 	if( iBucket >= pEngine->split_bucket + pEngine->max_split_bucket ){
50988 		/* Low mask */
50989 		iBucket = nHash & (pEngine->max_split_bucket - 1);
50990 	}
50991 	/* Map the logical bucket number to real page number */
50992 	pRec = lhMapFindBucket(pEngine,iBucket);
50993 	if( pRec == 0 ){
50994 		/* Request a new page */
50995 		rc = lhAcquirePage(pEngine,&pRaw);
50996 		if( rc != UNQLITE_OK ){
50997 			return rc;
50998 		}
50999 		/* Initialize the page */
51000 		pPage = lhNewPage(pEngine,pRaw,0);
51001 		if( pPage == 0 ){
51002 			return UNQLITE_NOMEM;
51003 		}
51004 		/* Mark as an empty page */
51005 		rc = lhSetEmptyPage(pPage);
51006 		if( rc != UNQLITE_OK ){
51007 			pEngine->pIo->xPageUnref(pRaw); /* pPage will be released during this call */
51008 			return rc;
51009 		}
51010 		/* Store the cell */
51011 		rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
51012 		if( rc == UNQLITE_OK ){
51013 			/* Install and write the logical map record */
51014 			rc = lhMapWriteRecord(pEngine,iBucket,pRaw->pgno);
51015 		}
51016 		pEngine->pIo->xPageUnref(pRaw);
51017 		return rc;
51018 	}else{
51019 		/* Load the page */
51020 		rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
51021 		if( rc != UNQLITE_OK ){
51022 			/* IO error, unlikely scenario */
51023 			return rc;
51024 		}
51025 		/* Do not add this page to the hot dirty list */
51026 		pEngine->pIo->xDontMkHot(pPage->pRaw);
51027 		/* Lookup for the cell */
51028 		pCell = lhFindCell(pPage,pKey,(sxu32)nKeyLen,nHash);
51029 		if( pCell == 0 ){
51030 			/* Create the record */
51031 			rc = lhRecordInstall(pPage,nHash,pKey,nKeyLen,pData,nDataLen);
51032 			if( rc == SXERR_RETRY && iCnt++ < 2 ){
51033 				rc = UNQLITE_OK;
51034 				goto retry;
51035 			}
51036 		}else{
51037 			if( is_append ){
51038 				/* Append operation */
51039 				rc = lhRecordAppend(pCell,pData,nDataLen);
51040 			}else{
51041 				/* Overwrite old value */
51042 				rc = lhRecordOverwrite(pCell,pData,nDataLen);
51043 			}
51044 		}
51045 		pEngine->pIo->xPageUnref(pPage->pRaw);
51046 	}
51047 	return rc;
51048 }
51049 /*
51050  * Replace method.
51051  */
lhash_kv_replace(unqlite_kv_engine * pKv,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)51052 static int lhash_kv_replace(
51053 	  unqlite_kv_engine *pKv,
51054 	  const void *pKey,int nKeyLen,
51055 	  const void *pData,unqlite_int64 nDataLen
51056 	  )
51057 {
51058 	int rc;
51059 	rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,0);
51060 	return rc;
51061 }
51062 /*
51063  * Append method.
51064  */
lhash_kv_append(unqlite_kv_engine * pKv,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)51065 static int lhash_kv_append(
51066 	  unqlite_kv_engine *pKv,
51067 	  const void *pKey,int nKeyLen,
51068 	  const void *pData,unqlite_int64 nDataLen
51069 	  )
51070 {
51071 	int rc;
51072 	rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,1);
51073 	return rc;
51074 }
51075 /*
51076  * Write the hash header (Page one).
51077  */
lhash_write_header(lhash_kv_engine * pEngine,unqlite_page * pHeader)51078 static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
51079 {
51080 	unsigned char *zRaw = pHeader->zData;
51081 	lhash_bmap_page *pMap;
51082 
51083 	pEngine->pHeader = pHeader;
51084 	/* 4 byte magic number */
51085 	SyBigEndianPack32(zRaw,pEngine->nMagic);
51086 	zRaw += 4;
51087 	/* 4 byte hash value to identify a valid hash function */
51088 	SyBigEndianPack32(zRaw,pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1));
51089 	zRaw += 4;
51090 	/* List of free pages: Empty */
51091 	SyBigEndianPack64(zRaw,0);
51092 	zRaw += 8;
51093 	/* Current split bucket */
51094 	SyBigEndianPack64(zRaw,pEngine->split_bucket);
51095 	zRaw += 8;
51096 	/* Maximum split bucket */
51097 	SyBigEndianPack64(zRaw,pEngine->max_split_bucket);
51098 	zRaw += 8;
51099 	/* Initialiaze the bucket map */
51100 	pMap = &pEngine->sPageMap;
51101 	/* Fill in the structure */
51102 	pMap->iNum = pHeader->pgno;
51103 	/* Next page in the bucket map */
51104 	SyBigEndianPack64(zRaw,0);
51105 	zRaw += 8;
51106 	/* Total number of records in the bucket map */
51107 	SyBigEndianPack32(zRaw,0);
51108 	zRaw += 4;
51109 	pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
51110 	/* All done */
51111 	return UNQLITE_OK;
51112  }
51113 /*
51114  * Exported: xOpen() method.
51115  */
lhash_kv_open(unqlite_kv_engine * pEngine,pgno dbSize)51116 static int lhash_kv_open(unqlite_kv_engine *pEngine,pgno dbSize)
51117 {
51118 	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51119 	unqlite_page *pHeader;
51120 	int rc;
51121 	if( dbSize < 1 ){
51122 		/* A new database, create the header */
51123 		rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pHeader);
51124 		if( rc != UNQLITE_OK ){
51125 			return rc;
51126 		}
51127 		/* Acquire a writer lock */
51128 		rc = pEngine->pIo->xWrite(pHeader);
51129 		if( rc != UNQLITE_OK ){
51130 			return rc;
51131 		}
51132 		/* Write the hash header */
51133 		rc = lhash_write_header(pHash,pHeader);
51134 		if( rc != UNQLITE_OK ){
51135 			return rc;
51136 		}
51137 	}else{
51138 		/* Acquire the page one of the database */
51139 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,&pHeader);
51140 		if( rc != UNQLITE_OK ){
51141 			return rc;
51142 		}
51143 		/* Read the database header */
51144 		rc = lhash_read_header(pHash,pHeader);
51145 		if( rc != UNQLITE_OK ){
51146 			return rc;
51147 		}
51148 	}
51149 	return UNQLITE_OK;
51150 }
51151 /*
51152  * Release a master or slave page. (xUnpin callback).
51153  */
lhash_page_release(void * pUserData)51154 static void lhash_page_release(void *pUserData)
51155 {
51156 	lhpage *pPage = (lhpage *)pUserData;
51157 	lhash_kv_engine *pEngine = pPage->pHash;
51158 	lhcell *pNext,*pCell = pPage->pList;
51159 	unqlite_page *pRaw = pPage->pRaw;
51160 	sxu32 n;
51161 	/* Drop in-memory cells */
51162 	for( n = 0 ; n < pPage->nCell ; ++n ){
51163 		pNext = pCell->pNext;
51164 		SyBlobRelease(&pCell->sKey);
51165 		/* Release the cell instance */
51166 		SyMemBackendPoolFree(&pEngine->sAllocator,(void *)pCell);
51167 		/* Point to the next entry */
51168 		pCell = pNext;
51169 	}
51170 	if( pPage->apCell ){
51171 		/* Release the cell table */
51172 		SyMemBackendFree(&pEngine->sAllocator,(void *)pPage->apCell);
51173 	}
51174 	/* Finally, release the whole page */
51175 	SyMemBackendPoolFree(&pEngine->sAllocator,pPage);
51176 	pRaw->pUserData = 0;
51177 }
51178 /*
51179  * Default hash function (DJB).
51180  */
lhash_bin_hash(const void * pSrc,sxu32 nLen)51181 static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen)
51182 {
51183 	register unsigned char *zIn = (unsigned char *)pSrc;
51184 	unsigned char *zEnd;
51185 	sxu32 nH = 5381;
51186 	if( nLen > 2048 /* 2K */ ){
51187 		nLen = 2048;
51188 	}
51189 	zEnd = &zIn[nLen];
51190 	for(;;){
51191 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51192 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51193 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51194 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
51195 	}
51196 	return nH;
51197 }
51198 /*
51199  * Exported: xInit() method.
51200  * Initialize the Key value storage engine.
51201  */
lhash_kv_init(unqlite_kv_engine * pEngine,int iPageSize)51202 static int lhash_kv_init(unqlite_kv_engine *pEngine,int iPageSize)
51203 {
51204 	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51205 	int rc;
51206 
51207 	/* This structure is always zeroed, go to the initialization directly */
51208 	SyMemBackendInitFromParent(&pHash->sAllocator,unqliteExportMemBackend());
51209 #if defined(UNQLITE_ENABLE_THREADS)
51210 	/* Already protected by the upper layers */
51211 	SyMemBackendDisbaleMutexing(&pHash->sAllocator);
51212 #endif
51213 	pHash->iPageSize = iPageSize;
51214 	/* Default hash function */
51215 	pHash->xHash = lhash_bin_hash;
51216 	/* Default comparison function */
51217 	pHash->xCmp = SyMemcmp;
51218 	/* Allocate a new record map */
51219 	pHash->nBuckSize = 32;
51220 	pHash->apMap = (lhash_bmap_rec **)SyMemBackendAlloc(&pHash->sAllocator,pHash->nBuckSize *sizeof(lhash_bmap_rec *));
51221 	if( pHash->apMap == 0 ){
51222 		rc = UNQLITE_NOMEM;
51223 		goto err;
51224 	}
51225 	/* Zero the table */
51226 	SyZero(pHash->apMap,pHash->nBuckSize * sizeof(lhash_bmap_rec *));
51227 	/* Linear hashing components */
51228 	pHash->split_bucket = 0; /* Logical not real bucket number */
51229 	pHash->max_split_bucket = 1;
51230 	pHash->nmax_split_nucket = 2;
51231 	pHash->nMagic = L_HASH_MAGIC;
51232 	/* Install the cache unpin and reload callbacks */
51233 	pHash->pIo->xSetUnpin(pHash->pIo->pHandle,lhash_page_release);
51234 	pHash->pIo->xSetReload(pHash->pIo->pHandle,lhash_page_release);
51235 	return UNQLITE_OK;
51236 err:
51237 	SyMemBackendRelease(&pHash->sAllocator);
51238 	return rc;
51239 }
51240 /*
51241  * Exported: xRelease() method.
51242  * Release the Key value storage engine.
51243  */
lhash_kv_release(unqlite_kv_engine * pEngine)51244 static void lhash_kv_release(unqlite_kv_engine *pEngine)
51245 {
51246 	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51247 	/* Release the private memory backend */
51248 	SyMemBackendRelease(&pHash->sAllocator);
51249 }
51250 /*
51251  *  Exported: xConfig() method.
51252  *  Configure the linear hash KV store.
51253  */
lhash_kv_config(unqlite_kv_engine * pEngine,int op,va_list ap)51254 static int lhash_kv_config(unqlite_kv_engine *pEngine,int op,va_list ap)
51255 {
51256 	lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
51257 	int rc = UNQLITE_OK;
51258 	switch(op){
51259 	case UNQLITE_KV_CONFIG_HASH_FUNC: {
51260 		/* Default hash function */
51261 		if( pHash->nBuckRec > 0 ){
51262 			/* Locked operation */
51263 			rc = UNQLITE_LOCKED;
51264 		}else{
51265 			ProcHash xHash = va_arg(ap,ProcHash);
51266 			if( xHash ){
51267 				pHash->xHash = xHash;
51268 			}
51269 		}
51270 		break;
51271 									  }
51272 	case UNQLITE_KV_CONFIG_CMP_FUNC: {
51273 		/* Default comparison function */
51274 		ProcCmp xCmp = va_arg(ap,ProcCmp);
51275 		if( xCmp ){
51276 			pHash->xCmp  = xCmp;
51277 		}
51278 		break;
51279 									 }
51280 	default:
51281 		/* Unknown OP */
51282 		rc = UNQLITE_UNKNOWN;
51283 		break;
51284 	}
51285 	return rc;
51286 }
51287 /*
51288  * Each public cursor is identified by an instance of this structure.
51289  */
51290 typedef struct lhash_kv_cursor lhash_kv_cursor;
51291 struct lhash_kv_cursor
51292 {
51293 	unqlite_kv_engine *pStore; /* Must be first */
51294 	/* Private fields */
51295 	int iState;           /* Current state of the cursor */
51296 	int is_first;         /* True to read the database header */
51297 	lhcell *pCell;        /* Current cell we are processing */
51298 	unqlite_page *pRaw;   /* Raw disk page */
51299 	lhash_bmap_rec *pRec; /* Logical to real bucket map */
51300 };
51301 /*
51302  * Possible state of the cursor
51303  */
51304 #define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */
51305 #define L_HASH_CURSOR_STATE_CELL      2 /* Processing Cell */
51306 #define L_HASH_CURSOR_STATE_DONE      3 /* Cursor does not point to anything */
51307 /*
51308  * Initialize the cursor.
51309  */
lhInitCursor(unqlite_kv_cursor * pPtr)51310 static void lhInitCursor(unqlite_kv_cursor *pPtr)
51311 {
51312 	 lhash_kv_engine *pEngine = (lhash_kv_engine *)pPtr->pStore;
51313 	 lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51314 	 /* Init */
51315 	 pCur->iState = L_HASH_CURSOR_STATE_NEXT_PAGE;
51316 	 pCur->pCell = 0;
51317 	 pCur->pRec = pEngine->pFirst;
51318 	 pCur->pRaw = 0;
51319 	 pCur->is_first = 1;
51320 }
51321 /*
51322  * Point to the next page on the database.
51323  */
lhCursorNextPage(lhash_kv_cursor * pPtr)51324 static int lhCursorNextPage(lhash_kv_cursor *pPtr)
51325 {
51326 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51327 	lhash_bmap_rec *pRec;
51328 	lhpage *pPage;
51329 	int rc;
51330 	for(;;){
51331 		pRec = pCur->pRec;
51332 		if( pRec == 0 ){
51333 			pCur->iState = L_HASH_CURSOR_STATE_DONE;
51334 			return UNQLITE_DONE;
51335 		}
51336 		if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
51337 			/* Unref this page */
51338 			pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
51339 			pPtr->pRaw = 0;
51340 		}
51341 		/* Advance the map cursor */
51342 		pCur->pRec = pRec->pPrev; /* Not a bug, reverse link */
51343 		/* Load the next page on the list */
51344 		rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
51345 		if( rc != UNQLITE_OK ){
51346 			return rc;
51347 		}
51348 		if( pPage->pList ){
51349 			/* Reflect the change */
51350 			pCur->pCell = pPage->pList;
51351 			pCur->iState = L_HASH_CURSOR_STATE_CELL;
51352 			pCur->pRaw = pPage->pRaw;
51353 			break;
51354 		}
51355 		/* Empty page, discard this page and continue */
51356 		pPage->pHash->pIo->xPageUnref(pPage->pRaw);
51357 	}
51358 	return UNQLITE_OK;
51359 }
51360 /*
51361  * Point to the previous page on the database.
51362  */
lhCursorPrevPage(lhash_kv_cursor * pPtr)51363 static int lhCursorPrevPage(lhash_kv_cursor *pPtr)
51364 {
51365 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51366 	lhash_bmap_rec *pRec;
51367 	lhpage *pPage;
51368 	int rc;
51369 	for(;;){
51370 		pRec = pCur->pRec;
51371 		if( pRec == 0 ){
51372 			pCur->iState = L_HASH_CURSOR_STATE_DONE;
51373 			return UNQLITE_DONE;
51374 		}
51375 		if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
51376 			/* Unref this page */
51377 			pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
51378 			pPtr->pRaw = 0;
51379 		}
51380 		/* Advance the map cursor */
51381 		pCur->pRec = pRec->pNext; /* Not a bug, reverse link */
51382 		/* Load the previous page on the list */
51383 		rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
51384 		if( rc != UNQLITE_OK ){
51385 			return rc;
51386 		}
51387 		if( pPage->pFirst ){
51388 			/* Reflect the change */
51389 			pCur->pCell = pPage->pFirst;
51390 			pCur->iState = L_HASH_CURSOR_STATE_CELL;
51391 			pCur->pRaw = pPage->pRaw;
51392 			break;
51393 		}
51394 		/* Discard this page and continue */
51395 		pPage->pHash->pIo->xPageUnref(pPage->pRaw);
51396 	}
51397 	return UNQLITE_OK;
51398 }
51399 /*
51400  * Is a valid cursor.
51401  */
lhCursorValid(unqlite_kv_cursor * pPtr)51402 static int lhCursorValid(unqlite_kv_cursor *pPtr)
51403 {
51404 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
51405 	return (pCur->iState == L_HASH_CURSOR_STATE_CELL) && pCur->pCell;
51406 }
51407 /*
51408  * Point to the first record.
51409  */
lhCursorFirst(unqlite_kv_cursor * pCursor)51410 static int lhCursorFirst(unqlite_kv_cursor *pCursor)
51411 {
51412 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51413 	lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
51414 	int rc;
51415 	if( pCur->is_first ){
51416 		/* Read the database header first */
51417 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
51418 		if( rc != UNQLITE_OK ){
51419 			return rc;
51420 		}
51421 		pCur->is_first = 0;
51422 	}
51423 	/* Point to the first map record */
51424 	pCur->pRec = pEngine->pFirst;
51425 	/* Load the cells */
51426 	rc = lhCursorNextPage(pCur);
51427 	return rc;
51428 }
51429 /*
51430  * Point to the last record.
51431  */
lhCursorLast(unqlite_kv_cursor * pCursor)51432 static int lhCursorLast(unqlite_kv_cursor *pCursor)
51433 {
51434 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51435 	lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
51436 	int rc;
51437 	if( pCur->is_first ){
51438 		/* Read the database header first */
51439 		rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
51440 		if( rc != UNQLITE_OK ){
51441 			return rc;
51442 		}
51443 		pCur->is_first = 0;
51444 	}
51445 	/* Point to the last map record */
51446 	pCur->pRec = pEngine->pList;
51447 	/* Load the cells */
51448 	rc = lhCursorPrevPage(pCur);
51449 	return rc;
51450 }
51451 /*
51452  * Reset the cursor.
51453  */
lhCursorReset(unqlite_kv_cursor * pCursor)51454 static void lhCursorReset(unqlite_kv_cursor *pCursor)
51455 {
51456 	lhCursorFirst(pCursor);
51457 }
51458 /*
51459  * Point to the next record.
51460  */
lhCursorNext(unqlite_kv_cursor * pCursor)51461 static int lhCursorNext(unqlite_kv_cursor *pCursor)
51462 {
51463 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51464 	lhcell *pCell;
51465 	int rc;
51466 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51467 		/* Load the cells of the next page  */
51468 		rc = lhCursorNextPage(pCur);
51469 		return rc;
51470 	}
51471 	pCell = pCur->pCell;
51472 	pCur->pCell = pCell->pNext;
51473 	if( pCur->pCell == 0 ){
51474 		/* Load the cells of the next page  */
51475 		rc = lhCursorNextPage(pCur);
51476 		return rc;
51477 	}
51478 	return UNQLITE_OK;
51479 }
51480 /*
51481  * Point to the previous record.
51482  */
lhCursorPrev(unqlite_kv_cursor * pCursor)51483 static int lhCursorPrev(unqlite_kv_cursor *pCursor)
51484 {
51485 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51486 	lhcell *pCell;
51487 	int rc;
51488 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51489 		/* Load the cells of the previous page  */
51490 		rc = lhCursorPrevPage(pCur);
51491 		return rc;
51492 	}
51493 	pCell = pCur->pCell;
51494 	pCur->pCell = pCell->pPrev;
51495 	if( pCur->pCell == 0 ){
51496 		/* Load the cells of the previous page  */
51497 		rc = lhCursorPrevPage(pCur);
51498 		return rc;
51499 	}
51500 	return UNQLITE_OK;
51501 }
51502 /*
51503  * Return key length.
51504  */
lhCursorKeyLength(unqlite_kv_cursor * pCursor,int * pLen)51505 static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
51506 {
51507 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51508 	lhcell *pCell;
51509 
51510 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51511 		/* Invalid state */
51512 		return UNQLITE_INVALID;
51513 	}
51514 	/* Point to the target cell */
51515 	pCell = pCur->pCell;
51516 	/* Return key length */
51517 	*pLen = (int)pCell->nKey;
51518 	return UNQLITE_OK;
51519 }
51520 /*
51521  * Return data length.
51522  */
lhCursorDataLength(unqlite_kv_cursor * pCursor,unqlite_int64 * pLen)51523 static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
51524 {
51525 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51526 	lhcell *pCell;
51527 
51528 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51529 		/* Invalid state */
51530 		return UNQLITE_INVALID;
51531 	}
51532 	/* Point to the target cell */
51533 	pCell = pCur->pCell;
51534 	/* Return data length */
51535 	*pLen = (unqlite_int64)pCell->nData;
51536 	return UNQLITE_OK;
51537 }
51538 /*
51539  * Consume the key.
51540  */
lhCursorKey(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)51541 static int lhCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
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 		/* Invalid state */
51548 		return UNQLITE_INVALID;
51549 	}
51550 	/* Point to the target cell */
51551 	pCell = pCur->pCell;
51552 	if( SyBlobLength(&pCell->sKey) > 0 ){
51553 		/* Consume the key directly */
51554 		rc = xConsumer(SyBlobData(&pCell->sKey),SyBlobLength(&pCell->sKey),pUserData);
51555 	}else{
51556 		/* Very large key */
51557 		rc = lhConsumeCellkey(pCell,xConsumer,pUserData,0);
51558 	}
51559 	return rc;
51560 }
51561 /*
51562  * Consume the data.
51563  */
lhCursorData(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)51564 static int lhCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
51565 {
51566 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51567 	lhcell *pCell;
51568 	int rc;
51569 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51570 		/* Invalid state */
51571 		return UNQLITE_INVALID;
51572 	}
51573 	/* Point to the target cell */
51574 	pCell = pCur->pCell;
51575 	/* Consume the data */
51576 	rc = lhConsumeCellData(pCell,xConsumer,pUserData);
51577 	return rc;
51578 }
51579 /*
51580  * Find a partiuclar record.
51581  */
lhCursorSeek(unqlite_kv_cursor * pCursor,const void * pKey,int nByte,int iPos)51582 static int lhCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
51583 {
51584 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51585 	int rc;
51586 	/* Perform a lookup */
51587 	rc = lhRecordLookup((lhash_kv_engine *)pCur->pStore,pKey,nByte,&pCur->pCell);
51588 	if( rc != UNQLITE_OK ){
51589 		SXUNUSED(iPos);
51590 		pCur->pCell = 0;
51591 		pCur->iState = L_HASH_CURSOR_STATE_DONE;
51592 		return rc;
51593 	}
51594 	pCur->iState = L_HASH_CURSOR_STATE_CELL;
51595 	return UNQLITE_OK;
51596 }
51597 /*
51598  * Remove a particular record.
51599  */
lhCursorDelete(unqlite_kv_cursor * pCursor)51600 static int lhCursorDelete(unqlite_kv_cursor *pCursor)
51601 {
51602 	lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
51603 	lhcell *pCell;
51604 	int rc;
51605 	if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
51606 		/* Invalid state */
51607 		return UNQLITE_INVALID;
51608 	}
51609 	/* Point to the target cell  */
51610 	pCell = pCur->pCell;
51611 	/* Point to the next entry */
51612 	pCur->pCell = pCell->pNext;
51613 	/* Perform the deletion */
51614 	rc = lhRecordRemove(pCell);
51615 	return rc;
51616 }
51617 /*
51618  * Export the linear-hash storage engine.
51619  */
unqliteExportDiskKvStorage(void)51620 UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void)
51621 {
51622 	static const unqlite_kv_methods sDiskStore = {
51623 		"hash",                     /* zName */
51624 		sizeof(lhash_kv_engine),    /* szKv */
51625 		sizeof(lhash_kv_cursor),    /* szCursor */
51626 		1,                          /* iVersion */
51627 		lhash_kv_init,              /* xInit */
51628 		lhash_kv_release,           /* xRelease */
51629 		lhash_kv_config,            /* xConfig */
51630 		lhash_kv_open,              /* xOpen */
51631 		lhash_kv_replace,           /* xReplace */
51632 		lhash_kv_append,            /* xAppend */
51633 		lhInitCursor,               /* xCursorInit */
51634 		lhCursorSeek,               /* xSeek */
51635 		lhCursorFirst,              /* xFirst */
51636 		lhCursorLast,               /* xLast */
51637 		lhCursorValid,              /* xValid */
51638 		lhCursorNext,               /* xNext */
51639 		lhCursorPrev,               /* xPrev */
51640 		lhCursorDelete,             /* xDelete */
51641 		lhCursorKeyLength,          /* xKeyLength */
51642 		lhCursorKey,                /* xKey */
51643 		lhCursorDataLength,         /* xDataLength */
51644 		lhCursorData,               /* xData */
51645 		lhCursorReset,              /* xReset */
51646 		0                           /* xRelease */
51647 	};
51648 	return &sDiskStore;
51649 }
51650 
51651 /* mem_kv.c */
51652 /*
51653  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
51654  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
51655  * Version 1.1.6
51656  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
51657  * please contact Symisc Systems via:
51658  *       legal@symisc.net
51659  *       licensing@symisc.net
51660  *       contact@symisc.net
51661  * or visit:
51662  *      http://unqlite.org/licensing.html
51663  */
51664  /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable <chm@symisc.net> $ */
51665 #ifndef UNQLITE_AMALGAMATION
51666 #include "unqliteInt.h"
51667 #endif
51668 /*
51669  * This file implements an in-memory key value storage engine for unQLite.
51670  * Note that this storage engine does not support transactions.
51671  *
51672  * Normaly, I (chm@symisc.net) planned to implement a red-black tree
51673  * which is suitable for this kind of operation, but due to the lack
51674  * of time, I decided to implement a tunned hashtable which everybody
51675  * know works very well for this kind of operation.
51676  * Again, I insist on a red-black tree implementation for future version
51677  * of Unqlite.
51678  */
51679 /* Forward declaration */
51680 typedef struct mem_hash_kv_engine mem_hash_kv_engine;
51681 /*
51682  * Each record is storead in an instance of the following structure.
51683  */
51684 typedef struct mem_hash_record mem_hash_record;
51685 struct mem_hash_record
51686 {
51687 	mem_hash_kv_engine *pEngine;    /* Storage engine */
51688 	sxu32 nHash;                    /* Hash of the key */
51689 	const void *pKey;               /* Key */
51690 	sxu32 nKeyLen;                  /* Key size (Max 1GB) */
51691 	const void *pData;              /* Data */
51692 	sxu32 nDataLen;                 /* Data length (Max 4GB) */
51693 	mem_hash_record *pNext,*pPrev;  /* Link to other records */
51694 	mem_hash_record *pNextHash,*pPrevHash; /* Collision link */
51695 };
51696 /*
51697  * Each in-memory KV engine is represented by an instance
51698  * of the following structure.
51699  */
51700 struct mem_hash_kv_engine
51701 {
51702 	const unqlite_kv_io *pIo; /* IO methods: MUST be first */
51703 	/* Private data */
51704 	SyMemBackend sAlloc;        /* Private memory allocator */
51705 	ProcHash    xHash;          /* Default hash function */
51706 	ProcCmp     xCmp;           /* Default comparison function */
51707 	sxu32 nRecord;              /* Total number of records  */
51708 	sxu32 nBucket;              /* Bucket size: Must be a power of two */
51709 	mem_hash_record **apBucket; /* Hash bucket */
51710 	mem_hash_record *pFirst;    /* First inserted entry */
51711 	mem_hash_record *pLast;     /* Last inserted entry */
51712 };
51713 /*
51714  * Allocate a new hash record.
51715  */
MemHashNewRecord(mem_hash_kv_engine * pEngine,const void * pKey,int nKey,const void * pData,unqlite_int64 nData,sxu32 nHash)51716 static mem_hash_record * MemHashNewRecord(
51717 	mem_hash_kv_engine *pEngine,
51718 	const void *pKey,int nKey,
51719 	const void *pData,unqlite_int64 nData,
51720 	sxu32 nHash
51721 	)
51722 {
51723 	SyMemBackend *pAlloc = &pEngine->sAlloc;
51724 	mem_hash_record *pRecord;
51725 	void *pDupData;
51726 	sxu32 nByte;
51727 	char *zPtr;
51728 
51729 	/* Total number of bytes to alloc */
51730 	nByte = sizeof(mem_hash_record) + nKey;
51731 	/* Allocate a new instance */
51732 	pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte);
51733 	if( pRecord == 0 ){
51734 		return 0;
51735 	}
51736 	pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData);
51737 	if( pDupData == 0 ){
51738 		SyMemBackendFree(pAlloc,pRecord);
51739 		return 0;
51740 	}
51741 	zPtr = (char *)pRecord;
51742 	zPtr += sizeof(mem_hash_record);
51743 	/* Zero the structure */
51744 	SyZero(pRecord,sizeof(mem_hash_record));
51745 	/* Fill in the structure */
51746 	pRecord->pEngine = pEngine;
51747 	pRecord->nDataLen = (sxu32)nData;
51748 	pRecord->nKeyLen = (sxu32)nKey;
51749 	pRecord->nHash = nHash;
51750 	SyMemcpy(pKey,zPtr,pRecord->nKeyLen);
51751 	pRecord->pKey = (const void *)zPtr;
51752 	SyMemcpy(pData,pDupData,pRecord->nDataLen);
51753 	pRecord->pData = pDupData;
51754 	/* All done */
51755 	return pRecord;
51756 }
51757 /*
51758  * Install a given record in the hashtable.
51759  */
MemHashLinkRecord(mem_hash_kv_engine * pEngine,mem_hash_record * pRecord)51760 static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord)
51761 {
51762 	sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1);
51763 	pRecord->pNextHash = pEngine->apBucket[nBucket];
51764 	if( pEngine->apBucket[nBucket] ){
51765 		pEngine->apBucket[nBucket]->pPrevHash = pRecord;
51766 	}
51767 	pEngine->apBucket[nBucket] = pRecord;
51768 	if( pEngine->pFirst == 0 ){
51769 		pEngine->pFirst = pEngine->pLast = pRecord;
51770 	}else{
51771 		MACRO_LD_PUSH(pEngine->pLast,pRecord);
51772 	}
51773 	pEngine->nRecord++;
51774 }
51775 /*
51776  * Unlink a given record from the hashtable.
51777  */
MemHashUnlinkRecord(mem_hash_kv_engine * pEngine,mem_hash_record * pEntry)51778 static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry)
51779 {
51780 	sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1);
51781 	SyMemBackend *pAlloc = &pEngine->sAlloc;
51782 	if( pEntry->pPrevHash == 0 ){
51783 		pEngine->apBucket[nBucket] = pEntry->pNextHash;
51784 	}else{
51785 		pEntry->pPrevHash->pNextHash = pEntry->pNextHash;
51786 	}
51787 	if( pEntry->pNextHash ){
51788 		pEntry->pNextHash->pPrevHash = pEntry->pPrevHash;
51789 	}
51790 	MACRO_LD_REMOVE(pEngine->pLast,pEntry);
51791 	if( pEntry == pEngine->pFirst ){
51792 		pEngine->pFirst = pEntry->pPrev;
51793 	}
51794 	pEngine->nRecord--;
51795 	/* Release the entry */
51796 	SyMemBackendFree(pAlloc,(void *)pEntry->pData);
51797 	SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */
51798 }
51799 /*
51800  * Perform a lookup for a given entry.
51801  */
MemHashGetEntry(mem_hash_kv_engine * pEngine,const void * pKey,int nKeyLen)51802 static mem_hash_record * MemHashGetEntry(
51803 	mem_hash_kv_engine *pEngine,
51804 	const void *pKey,int nKeyLen
51805 	)
51806 {
51807 	mem_hash_record *pEntry;
51808 	sxu32 nHash,nBucket;
51809 	/* Hash the entry */
51810 	nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
51811 	nBucket = nHash & (pEngine->nBucket - 1);
51812 	pEntry = pEngine->apBucket[nBucket];
51813 	for(;;){
51814 		if( pEntry == 0 ){
51815 			break;
51816 		}
51817 		if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen &&
51818 			pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){
51819 				return pEntry;
51820 		}
51821 		pEntry = pEntry->pNextHash;
51822 	}
51823 	/* No such entry */
51824 	return 0;
51825 }
51826 /*
51827  * Rehash all the entries in the given table.
51828  */
MemHashGrowTable(mem_hash_kv_engine * pEngine)51829 static int MemHashGrowTable(mem_hash_kv_engine *pEngine)
51830 {
51831 	sxu32 nNewSize = pEngine->nBucket << 1;
51832 	mem_hash_record *pEntry;
51833 	mem_hash_record **apNew;
51834 	sxu32 n,iBucket;
51835 	/* Allocate a new larger table */
51836 	apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *));
51837 	if( apNew == 0 ){
51838 		/* Not so fatal, simply a performance hit */
51839 		return UNQLITE_OK;
51840 	}
51841 	/* Zero the new table */
51842 	SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *));
51843 	/* Rehash all entries */
51844 	n = 0;
51845 	pEntry = pEngine->pLast;
51846 	for(;;){
51847 
51848 		/* Loop one */
51849 		if( n >= pEngine->nRecord ){
51850 			break;
51851 		}
51852 		pEntry->pNextHash = pEntry->pPrevHash = 0;
51853 		/* Install in the new bucket */
51854 		iBucket = pEntry->nHash & (nNewSize - 1);
51855 		pEntry->pNextHash = apNew[iBucket];
51856 		if( apNew[iBucket] ){
51857 			apNew[iBucket]->pPrevHash = pEntry;
51858 		}
51859 		apNew[iBucket] = pEntry;
51860 		/* Point to the next entry */
51861 		pEntry = pEntry->pNext;
51862 		n++;
51863 
51864 		/* Loop two */
51865 		if( n >= pEngine->nRecord ){
51866 			break;
51867 		}
51868 		pEntry->pNextHash = pEntry->pPrevHash = 0;
51869 		/* Install in the new bucket */
51870 		iBucket = pEntry->nHash & (nNewSize - 1);
51871 		pEntry->pNextHash = apNew[iBucket];
51872 		if( apNew[iBucket] ){
51873 			apNew[iBucket]->pPrevHash = pEntry;
51874 		}
51875 		apNew[iBucket] = pEntry;
51876 		/* Point to the next entry */
51877 		pEntry = pEntry->pNext;
51878 		n++;
51879 
51880 		/* Loop three */
51881 		if( n >= pEngine->nRecord ){
51882 			break;
51883 		}
51884 		pEntry->pNextHash = pEntry->pPrevHash = 0;
51885 		/* Install in the new bucket */
51886 		iBucket = pEntry->nHash & (nNewSize - 1);
51887 		pEntry->pNextHash = apNew[iBucket];
51888 		if( apNew[iBucket] ){
51889 			apNew[iBucket]->pPrevHash = pEntry;
51890 		}
51891 		apNew[iBucket] = pEntry;
51892 		/* Point to the next entry */
51893 		pEntry = pEntry->pNext;
51894 		n++;
51895 
51896 		/* Loop four */
51897 		if( n >= pEngine->nRecord ){
51898 			break;
51899 		}
51900 		pEntry->pNextHash = pEntry->pPrevHash = 0;
51901 		/* Install in the new bucket */
51902 		iBucket = pEntry->nHash & (nNewSize - 1);
51903 		pEntry->pNextHash = apNew[iBucket];
51904 		if( apNew[iBucket] ){
51905 			apNew[iBucket]->pPrevHash = pEntry;
51906 		}
51907 		apNew[iBucket] = pEntry;
51908 		/* Point to the next entry */
51909 		pEntry = pEntry->pNext;
51910 		n++;
51911 	}
51912 	/* Release the old table and reflect the change */
51913 	SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket);
51914 	pEngine->apBucket = apNew;
51915 	pEngine->nBucket  = nNewSize;
51916 	return UNQLITE_OK;
51917 }
51918 /*
51919  * Exported Interfaces.
51920  */
51921 /*
51922  * Each public cursor is identified by an instance of this structure.
51923  */
51924 typedef struct mem_hash_cursor mem_hash_cursor;
51925 struct mem_hash_cursor
51926 {
51927 	unqlite_kv_engine *pStore; /* Must be first */
51928 	/* Private fields */
51929 	mem_hash_record *pCur;     /* Current hash record */
51930 };
51931 /*
51932  * Initialize the cursor.
51933  */
MemHashInitCursor(unqlite_kv_cursor * pCursor)51934 static void MemHashInitCursor(unqlite_kv_cursor *pCursor)
51935 {
51936 	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
51937 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51938 	 /* Point to the first inserted entry */
51939 	 pMem->pCur = pEngine->pFirst;
51940 }
51941 /*
51942  * Point to the first entry.
51943  */
MemHashCursorFirst(unqlite_kv_cursor * pCursor)51944 static int MemHashCursorFirst(unqlite_kv_cursor *pCursor)
51945 {
51946 	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
51947 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51948 	 pMem->pCur = pEngine->pFirst;
51949 	 return UNQLITE_OK;
51950 }
51951 /*
51952  * Point to the last entry.
51953  */
MemHashCursorLast(unqlite_kv_cursor * pCursor)51954 static int MemHashCursorLast(unqlite_kv_cursor *pCursor)
51955 {
51956 	 mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
51957 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51958 	 pMem->pCur = pEngine->pLast;
51959 	 return UNQLITE_OK;
51960 }
51961 /*
51962  * is a Valid Cursor.
51963  */
MemHashCursorValid(unqlite_kv_cursor * pCursor)51964 static int MemHashCursorValid(unqlite_kv_cursor *pCursor)
51965 {
51966 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51967 	 return pMem->pCur != 0 ? 1 : 0;
51968 }
51969 /*
51970  * Point to the next entry.
51971  */
MemHashCursorNext(unqlite_kv_cursor * pCursor)51972 static int MemHashCursorNext(unqlite_kv_cursor *pCursor)
51973 {
51974 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51975 	 if( pMem->pCur == 0){
51976 		 return UNQLITE_EOF;
51977 	 }
51978 	 pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */
51979 	 return UNQLITE_OK;
51980 }
51981 /*
51982  * Point to the previous entry.
51983  */
MemHashCursorPrev(unqlite_kv_cursor * pCursor)51984 static int MemHashCursorPrev(unqlite_kv_cursor *pCursor)
51985 {
51986 	 mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51987 	 if( pMem->pCur == 0){
51988 		 return UNQLITE_EOF;
51989 	 }
51990 	 pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */
51991 	 return UNQLITE_OK;
51992 }
51993 /*
51994  * Return key length.
51995  */
MemHashCursorKeyLength(unqlite_kv_cursor * pCursor,int * pLen)51996 static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
51997 {
51998 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
51999 	if( pMem->pCur == 0){
52000 		 return UNQLITE_EOF;
52001 	}
52002 	*pLen = (int)pMem->pCur->nKeyLen;
52003 	return UNQLITE_OK;
52004 }
52005 /*
52006  * Return data length.
52007  */
MemHashCursorDataLength(unqlite_kv_cursor * pCursor,unqlite_int64 * pLen)52008 static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
52009 {
52010 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52011 	if( pMem->pCur == 0 ){
52012 		 return UNQLITE_EOF;
52013 	}
52014 	*pLen = pMem->pCur->nDataLen;
52015 	return UNQLITE_OK;
52016 }
52017 /*
52018  * Consume the key.
52019  */
MemHashCursorKey(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)52020 static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
52021 {
52022 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52023 	int rc;
52024 	if( pMem->pCur == 0){
52025 		 return UNQLITE_EOF;
52026 	}
52027 	/* Invoke the callback */
52028 	rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData);
52029 	/* Callback result */
52030 	return rc;
52031 }
52032 /*
52033  * Consume the data.
52034  */
MemHashCursorData(unqlite_kv_cursor * pCursor,int (* xConsumer)(const void *,unsigned int,void *),void * pUserData)52035 static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
52036 {
52037 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52038 	int rc;
52039 	if( pMem->pCur == 0){
52040 		 return UNQLITE_EOF;
52041 	}
52042 	/* Invoke the callback */
52043 	rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData);
52044 	/* Callback result */
52045 	return rc;
52046 }
52047 /*
52048  * Reset the cursor.
52049  */
MemHashCursorReset(unqlite_kv_cursor * pCursor)52050 static void MemHashCursorReset(unqlite_kv_cursor *pCursor)
52051 {
52052 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52053 	pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst;
52054 }
52055 /*
52056  * Remove a particular record.
52057  */
MemHashCursorDelete(unqlite_kv_cursor * pCursor)52058 static int MemHashCursorDelete(unqlite_kv_cursor *pCursor)
52059 {
52060 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52061 	mem_hash_record *pNext;
52062 	if( pMem->pCur == 0 ){
52063 		/* Cursor does not point to anything */
52064 		return UNQLITE_NOTFOUND;
52065 	}
52066 	pNext = pMem->pCur->pPrev;
52067 	/* Perform the deletion */
52068 	MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur);
52069 	/* Point to the next entry */
52070 	pMem->pCur = pNext;
52071 	return UNQLITE_OK;
52072 }
52073 /*
52074  * Find a particular record.
52075  */
MemHashCursorSeek(unqlite_kv_cursor * pCursor,const void * pKey,int nByte,int iPos)52076 static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
52077 {
52078 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
52079 	mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
52080 	/* Perform the lookup */
52081 	pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte);
52082 	if( pMem->pCur == 0 ){
52083 		if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){
52084 			/* noop; */
52085 		}
52086 		/* No such record */
52087 		return UNQLITE_NOTFOUND;
52088 	}
52089 	return UNQLITE_OK;
52090 }
52091 /*
52092  * Builtin hash function.
52093  */
MemHashFunc(const void * pSrc,sxu32 nLen)52094 static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen)
52095 {
52096 	register unsigned char *zIn = (unsigned char *)pSrc;
52097 	unsigned char *zEnd;
52098 	sxu32 nH = 5381;
52099 	zEnd = &zIn[nLen];
52100 	for(;;){
52101 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52102 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52103 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52104 		if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
52105 	}
52106 	return nH;
52107 }
52108 /* Default bucket size */
52109 #define MEM_HASH_BUCKET_SIZE 64
52110 /* Default fill factor */
52111 #define MEM_HASH_FILL_FACTOR 4 /* or 3 */
52112 /*
52113  * Initialize the in-memory storage engine.
52114  */
MemHashInit(unqlite_kv_engine * pKvEngine,int iPageSize)52115 static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize)
52116 {
52117 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52118 	/* Note that this instance is already zeroed */
52119 	/* Memory backend */
52120 	SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend());
52121 #if defined(UNQLITE_ENABLE_THREADS)
52122 	/* Already protected by the upper layers */
52123 	SyMemBackendDisbaleMutexing(&pEngine->sAlloc);
52124 #endif
52125 	/* Default hash & comparison function */
52126 	pEngine->xHash = MemHashFunc;
52127 	pEngine->xCmp = SyMemcmp;
52128 	/* Allocate a new bucket */
52129 	pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
52130 	if( pEngine->apBucket == 0 ){
52131 		SXUNUSED(iPageSize); /* cc warning */
52132 		return UNQLITE_NOMEM;
52133 	}
52134 	/* Zero the bucket */
52135 	SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
52136 	pEngine->nRecord = 0;
52137 	pEngine->nBucket = MEM_HASH_BUCKET_SIZE;
52138 	return UNQLITE_OK;
52139 }
52140 /*
52141  * Release the in-memory storage engine.
52142  */
MemHashRelease(unqlite_kv_engine * pKvEngine)52143 static void MemHashRelease(unqlite_kv_engine *pKvEngine)
52144 {
52145 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52146 	/* Release the private memory backend */
52147 	SyMemBackendRelease(&pEngine->sAlloc);
52148 }
52149 /*
52150  * Configure the in-memory storage engine.
52151  */
MemHashConfigure(unqlite_kv_engine * pKvEngine,int iOp,va_list ap)52152 static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap)
52153 {
52154 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
52155 	int rc = UNQLITE_OK;
52156 	switch(iOp){
52157 	case UNQLITE_KV_CONFIG_HASH_FUNC:{
52158 		/* Use a default hash function */
52159 		if( pEngine->nRecord > 0 ){
52160 			rc = UNQLITE_LOCKED;
52161 		}else{
52162 			ProcHash xHash = va_arg(ap,ProcHash);
52163 			if( xHash ){
52164 				pEngine->xHash = xHash;
52165 			}
52166 		}
52167 		break;
52168 									 }
52169 	case UNQLITE_KV_CONFIG_CMP_FUNC: {
52170 		/* Default comparison function */
52171 		ProcCmp xCmp = va_arg(ap,ProcCmp);
52172 		if( xCmp ){
52173 			pEngine->xCmp = xCmp;
52174 		}
52175 		break;
52176 									 }
52177 	default:
52178 		/* Unknown configuration option */
52179 		rc = UNQLITE_UNKNOWN;
52180 	}
52181 	return rc;
52182 }
52183 /*
52184  * Replace method.
52185  */
MemHashReplace(unqlite_kv_engine * pKv,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)52186 static int MemHashReplace(
52187 	  unqlite_kv_engine *pKv,
52188 	  const void *pKey,int nKeyLen,
52189 	  const void *pData,unqlite_int64 nDataLen
52190 	  )
52191 {
52192 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
52193 	mem_hash_record *pRecord;
52194 	if( nDataLen > SXU32_HIGH ){
52195 		/* Database limit */
52196 		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
52197 		return UNQLITE_LIMIT;
52198 	}
52199 	/* Fetch the record first */
52200 	pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
52201 	if( pRecord == 0 ){
52202 		/* Allocate a new record */
52203 		pRecord = MemHashNewRecord(pEngine,
52204 			pKey,nKeyLen,
52205 			pData,nDataLen,
52206 			pEngine->xHash(pKey,nKeyLen)
52207 			);
52208 		if( pRecord == 0 ){
52209 			return UNQLITE_NOMEM;
52210 		}
52211 		/* Link the entry */
52212 		MemHashLinkRecord(pEngine,pRecord);
52213 		if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){
52214 			/* Rehash the table */
52215 			MemHashGrowTable(pEngine);
52216 		}
52217 	}else{
52218 		sxu32 nData = (sxu32)nDataLen;
52219 		void *pNew;
52220 		/* Replace an existing record */
52221 		if( nData == pRecord->nDataLen ){
52222 			/* No need to free the old chunk */
52223 			pNew = (void *)pRecord->pData;
52224 		}else{
52225 			pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData);
52226 			if( pNew == 0 ){
52227 				return UNQLITE_NOMEM;
52228 			}
52229 			/* Release the old data */
52230 			SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData);
52231 		}
52232 		/* Reflect the change */
52233 		pRecord->nDataLen = nData;
52234 		SyMemcpy(pData,pNew,nData);
52235 		pRecord->pData = pNew;
52236 	}
52237 	return UNQLITE_OK;
52238 }
52239 /*
52240  * Append method.
52241  */
MemHashAppend(unqlite_kv_engine * pKv,const void * pKey,int nKeyLen,const void * pData,unqlite_int64 nDataLen)52242 static int MemHashAppend(
52243 	  unqlite_kv_engine *pKv,
52244 	  const void *pKey,int nKeyLen,
52245 	  const void *pData,unqlite_int64 nDataLen
52246 	  )
52247 {
52248 	mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
52249 	mem_hash_record *pRecord;
52250 	if( nDataLen > SXU32_HIGH ){
52251 		/* Database limit */
52252 		pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
52253 		return UNQLITE_LIMIT;
52254 	}
52255 	/* Fetch the record first */
52256 	pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
52257 	if( pRecord == 0 ){
52258 		/* Allocate a new record */
52259 		pRecord = MemHashNewRecord(pEngine,
52260 			pKey,nKeyLen,
52261 			pData,nDataLen,
52262 			pEngine->xHash(pKey,nKeyLen)
52263 			);
52264 		if( pRecord == 0 ){
52265 			return UNQLITE_NOMEM;
52266 		}
52267 		/* Link the entry */
52268 		MemHashLinkRecord(pEngine,pRecord);
52269 		if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){
52270 			/* Rehash the table */
52271 			MemHashGrowTable(pEngine);
52272 		}
52273 	}else{
52274 		unqlite_int64 nNew = pRecord->nDataLen + nDataLen;
52275 		void *pOld = (void *)pRecord->pData;
52276 		sxu32 nData;
52277 		char *zNew;
52278 		/* Append data to the existing record */
52279 		if( nNew > SXU32_HIGH ){
52280 			/* Overflow */
52281 			pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
52282 			return UNQLITE_LIMIT;
52283 		}
52284 		nData = (sxu32)nNew;
52285 		/* Allocate bigger chunk */
52286 		zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData);
52287 		if( zNew == 0 ){
52288 			return UNQLITE_NOMEM;
52289 		}
52290 		/* Reflect the change */
52291 		SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen);
52292 		pRecord->pData = (const void *)zNew;
52293 		pRecord->nDataLen = nData;
52294 	}
52295 	return UNQLITE_OK;
52296 }
52297 /*
52298  * Export the in-memory storage engine.
52299  */
unqliteExportMemKvStorage(void)52300 UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void)
52301 {
52302 	static const unqlite_kv_methods sMemStore = {
52303 		"mem",                      /* zName */
52304 		sizeof(mem_hash_kv_engine), /* szKv */
52305 		sizeof(mem_hash_cursor),    /* szCursor */
52306 		1,                          /* iVersion */
52307 		MemHashInit,                /* xInit */
52308 		MemHashRelease,             /* xRelease */
52309 		MemHashConfigure,           /* xConfig */
52310 		0,                          /* xOpen */
52311 		MemHashReplace,             /* xReplace */
52312 		MemHashAppend,              /* xAppend */
52313 		MemHashInitCursor,          /* xCursorInit */
52314 		MemHashCursorSeek,          /* xSeek */
52315 		MemHashCursorFirst,         /* xFirst */
52316 		MemHashCursorLast,          /* xLast */
52317 		MemHashCursorValid,         /* xValid */
52318 		MemHashCursorNext,          /* xNext */
52319 		MemHashCursorPrev,          /* xPrev */
52320 		MemHashCursorDelete,        /* xDelete */
52321 		MemHashCursorKeyLength,     /* xKeyLength */
52322 		MemHashCursorKey,           /* xKey */
52323 		MemHashCursorDataLength,    /* xDataLength */
52324 		MemHashCursorData,          /* xData */
52325 		MemHashCursorReset,         /* xReset */
52326 		0        /* xRelease */
52327 	};
52328 	return &sMemStore;
52329 }
52330 
52331 /* os.c */
52332 /*
52333  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
52334  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
52335  * Version 1.1.6
52336  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
52337  * please contact Symisc Systems via:
52338  *       legal@symisc.net
52339  *       licensing@symisc.net
52340  *       contact@symisc.net
52341  * or visit:
52342  *      http://unqlite.org/licensing.html
52343  */
52344  /* $SymiscID: os.c v1.0 FreeBSD 2012-11-12 21:27 devel <chm@symisc.net> $ */
52345 #ifndef UNQLITE_AMALGAMATION
52346 #include "unqliteInt.h"
52347 #endif
52348 /* OS interfaces abstraction layers: Mostly SQLite3 source tree */
52349 /*
52350 ** The following routines are convenience wrappers around methods
52351 ** of the unqlite_file object.  This is mostly just syntactic sugar. All
52352 ** of this would be completely automatic if UnQLite were coded using
52353 ** C++ instead of plain old C.
52354 */
unqliteOsRead(unqlite_file * id,void * pBuf,unqlite_int64 amt,unqlite_int64 offset)52355 UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
52356 {
52357   return id->pMethods->xRead(id, pBuf, amt, offset);
52358 }
unqliteOsWrite(unqlite_file * id,const void * pBuf,unqlite_int64 amt,unqlite_int64 offset)52359 UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
52360 {
52361   return id->pMethods->xWrite(id, pBuf, amt, offset);
52362 }
unqliteOsTruncate(unqlite_file * id,unqlite_int64 size)52363 UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size)
52364 {
52365   return id->pMethods->xTruncate(id, size);
52366 }
unqliteOsSync(unqlite_file * id,int flags)52367 UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags)
52368 {
52369   return id->pMethods->xSync(id, flags);
52370 }
unqliteOsFileSize(unqlite_file * id,unqlite_int64 * pSize)52371 UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize)
52372 {
52373   return id->pMethods->xFileSize(id, pSize);
52374 }
unqliteOsLock(unqlite_file * id,int lockType)52375 UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType)
52376 {
52377   return id->pMethods->xLock(id, lockType);
52378 }
unqliteOsUnlock(unqlite_file * id,int lockType)52379 UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType)
52380 {
52381   return id->pMethods->xUnlock(id, lockType);
52382 }
unqliteOsCheckReservedLock(unqlite_file * id,int * pResOut)52383 UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut)
52384 {
52385   return id->pMethods->xCheckReservedLock(id, pResOut);
52386 }
unqliteOsSectorSize(unqlite_file * id)52387 UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id)
52388 {
52389   if( id->pMethods->xSectorSize ){
52390 	  return id->pMethods->xSectorSize(id);
52391   }
52392   return  UNQLITE_DEFAULT_SECTOR_SIZE;
52393 }
52394 /*
52395 ** The next group of routines are convenience wrappers around the
52396 ** VFS methods.
52397 */
unqliteOsOpen(unqlite_vfs * pVfs,SyMemBackend * pAlloc,const char * zPath,unqlite_file ** ppOut,unsigned int flags)52398 UNQLITE_PRIVATE int unqliteOsOpen(
52399   unqlite_vfs *pVfs,
52400   SyMemBackend *pAlloc,
52401   const char *zPath,
52402   unqlite_file **ppOut,
52403   unsigned int flags
52404 )
52405 {
52406 	unqlite_file *pFile;
52407 	int rc;
52408 	*ppOut = 0;
52409 	if( zPath == 0 ){
52410 		/* May happen if dealing with an in-memory database */
52411 		return SXERR_EMPTY;
52412 	}
52413 	/* Allocate a new instance */
52414 	pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile);
52415 	if( pFile == 0 ){
52416 		return UNQLITE_NOMEM;
52417 	}
52418 	/* Zero the structure */
52419 	SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile);
52420 	/* Invoke the xOpen method of the underlying VFS */
52421 	rc = pVfs->xOpen(pVfs, zPath, pFile, flags);
52422 	if( rc != UNQLITE_OK ){
52423 		SyMemBackendFree(pAlloc,pFile);
52424 		pFile = 0;
52425 	}
52426 	*ppOut = pFile;
52427 	return rc;
52428 }
unqliteOsCloseFree(SyMemBackend * pAlloc,unqlite_file * pId)52429 UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId)
52430 {
52431 	int rc = UNQLITE_OK;
52432 	if( pId ){
52433 		rc = pId->pMethods->xClose(pId);
52434 		SyMemBackendFree(pAlloc,pId);
52435 	}
52436 	return rc;
52437 }
unqliteOsDelete(unqlite_vfs * pVfs,const char * zPath,int dirSync)52438 UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync){
52439   return pVfs->xDelete(pVfs, zPath, dirSync);
52440 }
unqliteOsAccess(unqlite_vfs * pVfs,const char * zPath,int flags,int * pResOut)52441 UNQLITE_PRIVATE int unqliteOsAccess(
52442   unqlite_vfs *pVfs,
52443   const char *zPath,
52444   int flags,
52445   int *pResOut
52446 ){
52447   return pVfs->xAccess(pVfs, zPath, flags, pResOut);
52448 }
52449 
52450 /* os_unix.c */
52451 /*
52452  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
52453  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
52454  * Version 1.1.6
52455  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
52456  * please contact Symisc Systems via:
52457  *       legal@symisc.net
52458  *       licensing@symisc.net
52459  *       contact@symisc.net
52460  * or visit:
52461  *      http://unqlite.org/licensing.html
52462  */
52463  /* $SymiscID: os_unix.c v1.3 FreeBSD 2013-04-05 01:10 devel <chm@symisc.net> $ */
52464 #ifndef UNQLITE_AMALGAMATION
52465 #include "unqliteInt.h"
52466 #endif
52467 /*
52468  * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.).
52469  * Note: Mostly SQLite3 source tree.
52470  */
52471 #if defined(__UNIXES__)
52472 /** This file contains the VFS implementation for unix-like operating systems
52473 ** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others.
52474 **
52475 ** There are actually several different VFS implementations in this file.
52476 ** The differences are in the way that file locking is done.  The default
52477 ** implementation uses Posix Advisory Locks.  Alternative implementations
52478 ** use flock(), dot-files, various proprietary locking schemas, or simply
52479 ** skip locking all together.
52480 **
52481 ** This source file is organized into divisions where the logic for various
52482 ** subfunctions is contained within the appropriate division.  PLEASE
52483 ** KEEP THE STRUCTURE OF THIS FILE INTACT.  New code should be placed
52484 ** in the correct division and should be clearly labeled.
52485 **
52486 */
52487 /*
52488 ** standard include files.
52489 */
52490 #include <sys/types.h>
52491 #include <sys/stat.h>
52492 #include <sys/uio.h>
52493 #include <sys/file.h>
52494 #include <fcntl.h>
52495 #include <unistd.h>
52496 #include <time.h>
52497 #include <sys/time.h>
52498 #include <errno.h>
52499 #if defined(__APPLE__)
52500 # include <sys/mount.h>
52501 #endif
52502 /*
52503 ** Allowed values of unixFile.fsFlags
52504 */
52505 #define UNQLITE_FSFLAGS_IS_MSDOS     0x1
52506 
52507 /*
52508 ** Default permissions when creating a new file
52509 */
52510 #ifndef UNQLITE_DEFAULT_FILE_PERMISSIONS
52511 # define UNQLITE_DEFAULT_FILE_PERMISSIONS 0644
52512 #endif
52513 /*
52514  ** Default permissions when creating auto proxy dir
52515  */
52516 #ifndef UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS
52517 # define UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
52518 #endif
52519 /*
52520 ** Maximum supported path-length.
52521 */
52522 #define MAX_PATHNAME 512
52523 /*
52524 ** Only set the lastErrno if the error code is a real error and not
52525 ** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK
52526 */
52527 #define IS_LOCK_ERROR(x)  ((x != UNQLITE_OK) && (x != UNQLITE_BUSY))
52528 /* Forward references */
52529 typedef struct unixInodeInfo unixInodeInfo;   /* An i-node */
52530 typedef struct UnixUnusedFd UnixUnusedFd;     /* An unused file descriptor */
52531 /*
52532 ** Sometimes, after a file handle is closed by SQLite, the file descriptor
52533 ** cannot be closed immediately. In these cases, instances of the following
52534 ** structure are used to store the file descriptor while waiting for an
52535 ** opportunity to either close or reuse it.
52536 */
52537 struct UnixUnusedFd {
52538   int fd;                   /* File descriptor to close */
52539   int flags;                /* Flags this file descriptor was opened with */
52540   UnixUnusedFd *pNext;      /* Next unused file descriptor on same file */
52541 };
52542 /*
52543 ** The unixFile structure is subclass of unqlite3_file specific to the unix
52544 ** VFS implementations.
52545 */
52546 typedef struct unixFile unixFile;
52547 struct unixFile {
52548   const unqlite_io_methods *pMethod;  /* Always the first entry */
52549   unixInodeInfo *pInode;              /* Info about locks on this inode */
52550   int h;                              /* The file descriptor */
52551   int dirfd;                          /* File descriptor for the directory */
52552   unsigned char eFileLock;            /* The type of lock held on this fd */
52553   int lastErrno;                      /* The unix errno from last I/O error */
52554   void *lockingContext;               /* Locking style specific state */
52555   UnixUnusedFd *pUnused;              /* Pre-allocated UnixUnusedFd */
52556   int fileFlags;                      /* Miscellanous flags */
52557   const char *zPath;                  /* Name of the file */
52558   unsigned fsFlags;                   /* cached details from statfs() */
52559 };
52560 /*
52561 ** The following macros define bits in unixFile.fileFlags
52562 */
52563 #define UNQLITE_WHOLE_FILE_LOCKING  0x0001   /* Use whole-file locking */
52564 /*
52565 ** Define various macros that are missing from some systems.
52566 */
52567 #ifndef O_LARGEFILE
52568 # define O_LARGEFILE 0
52569 #endif
52570 #ifndef O_NOFOLLOW
52571 # define O_NOFOLLOW 0
52572 #endif
52573 #ifndef O_BINARY
52574 # define O_BINARY 0
52575 #endif
52576 /*
52577 ** Helper functions to obtain and relinquish the global mutex. The
52578 ** global mutex is used to protect the unixInodeInfo and
52579 ** vxworksFileId objects used by this file, all of which may be
52580 ** shared by multiple threads.
52581 **
52582 ** Function unixMutexHeld() is used to assert() that the global mutex
52583 ** is held when required. This function is only used as part of assert()
52584 ** statements. e.g.
52585 **
52586 **   unixEnterMutex()
52587 **     assert( unixMutexHeld() );
52588 **   unixEnterLeave()
52589 */
unixEnterMutex(void)52590 static void unixEnterMutex(void){
52591 #ifdef UNQLITE_ENABLE_THREADS
52592 	const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
52593 	if( pMutexMethods ){
52594 		SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
52595 		SyMutexEnter(pMutexMethods,pMutex);
52596 	}
52597 #endif /* UNQLITE_ENABLE_THREADS */
52598 }
unixLeaveMutex(void)52599 static void unixLeaveMutex(void){
52600 #ifdef UNQLITE_ENABLE_THREADS
52601   const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
52602   if( pMutexMethods ){
52603 	 SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
52604 	 SyMutexLeave(pMutexMethods,pMutex);
52605   }
52606 #endif /* UNQLITE_ENABLE_THREADS */
52607 }
52608 /*
52609 ** This routine translates a standard POSIX errno code into something
52610 ** useful to the clients of the unqlite3 functions.  Specifically, it is
52611 ** intended to translate a variety of "try again" errors into UNQLITE_BUSY
52612 ** and a variety of "please close the file descriptor NOW" errors into
52613 ** UNQLITE_IOERR
52614 **
52615 ** Errors during initialization of locks, or file system support for locks,
52616 ** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
52617 */
unqliteErrorFromPosixError(int posixError,int unqliteIOErr)52618 static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) {
52619   switch (posixError) {
52620   case 0:
52621     return UNQLITE_OK;
52622 
52623   case EAGAIN:
52624   case ETIMEDOUT:
52625   case EBUSY:
52626   case EINTR:
52627   case ENOLCK:
52628     /* random NFS retry error, unless during file system support
52629      * introspection, in which it actually means what it says */
52630     return UNQLITE_BUSY;
52631 
52632   case EACCES:
52633     /* EACCES is like EAGAIN during locking operations, but not any other time*/
52634       return UNQLITE_BUSY;
52635 
52636   case EPERM:
52637     return UNQLITE_PERM;
52638 
52639   case EDEADLK:
52640     return UNQLITE_IOERR;
52641 
52642 #if EOPNOTSUPP!=ENOTSUP
52643   case EOPNOTSUPP:
52644     /* something went terribly awry, unless during file system support
52645      * introspection, in which it actually means what it says */
52646 #endif
52647 #ifdef ENOTSUP
52648   case ENOTSUP:
52649     /* invalid fd, unless during file system support introspection, in which
52650      * it actually means what it says */
52651 #endif
52652   case EIO:
52653   case EBADF:
52654   case EINVAL:
52655   case ENOTCONN:
52656   case ENODEV:
52657   case ENXIO:
52658   case ENOENT:
52659   case ESTALE:
52660   case ENOSYS:
52661     /* these should force the client to close the file and reconnect */
52662 
52663   default:
52664     return unqliteIOErr;
52665   }
52666 }
52667 /******************************************************************************
52668 *************************** Posix Advisory Locking ****************************
52669 **
52670 ** POSIX advisory locks are broken by design.  ANSI STD 1003.1 (1996)
52671 ** section 6.5.2.2 lines 483 through 490 specify that when a process
52672 ** sets or clears a lock, that operation overrides any prior locks set
52673 ** by the same process.  It does not explicitly say so, but this implies
52674 ** that it overrides locks set by the same process using a different
52675 ** file descriptor.  Consider this test case:
52676 **
52677 **       int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
52678 **       int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
52679 **
52680 ** Suppose ./file1 and ./file2 are really the same file (because
52681 ** one is a hard or symbolic link to the other) then if you set
52682 ** an exclusive lock on fd1, then try to get an exclusive lock
52683 ** on fd2, it works.  I would have expected the second lock to
52684 ** fail since there was already a lock on the file due to fd1.
52685 ** But not so.  Since both locks came from the same process, the
52686 ** second overrides the first, even though they were on different
52687 ** file descriptors opened on different file names.
52688 **
52689 ** This means that we cannot use POSIX locks to synchronize file access
52690 ** among competing threads of the same process.  POSIX locks will work fine
52691 ** to synchronize access for threads in separate processes, but not
52692 ** threads within the same process.
52693 **
52694 ** To work around the problem, SQLite has to manage file locks internally
52695 ** on its own.  Whenever a new database is opened, we have to find the
52696 ** specific inode of the database file (the inode is determined by the
52697 ** st_dev and st_ino fields of the stat structure that fstat() fills in)
52698 ** and check for locks already existing on that inode.  When locks are
52699 ** created or removed, we have to look at our own internal record of the
52700 ** locks to see if another thread has previously set a lock on that same
52701 ** inode.
52702 **
52703 ** (Aside: The use of inode numbers as unique IDs does not work on VxWorks.
52704 ** For VxWorks, we have to use the alternative unique ID system based on
52705 ** canonical filename and implemented in the previous division.)
52706 **
52707 ** There is one locking structure
52708 ** per inode, so if the same inode is opened twice, both unixFile structures
52709 ** point to the same locking structure.  The locking structure keeps
52710 ** a reference count (so we will know when to delete it) and a "cnt"
52711 ** field that tells us its internal lock status.  cnt==0 means the
52712 ** file is unlocked.  cnt==-1 means the file has an exclusive lock.
52713 ** cnt>0 means there are cnt shared locks on the file.
52714 **
52715 ** Any attempt to lock or unlock a file first checks the locking
52716 ** structure.  The fcntl() system call is only invoked to set a
52717 ** POSIX lock if the internal lock structure transitions between
52718 ** a locked and an unlocked state.
52719 **
52720 ** But wait:  there are yet more problems with POSIX advisory locks.
52721 **
52722 ** If you close a file descriptor that points to a file that has locks,
52723 ** all locks on that file that are owned by the current process are
52724 ** released.  To work around this problem, each unixInodeInfo object
52725 ** maintains a count of the number of pending locks on that inode.
52726 ** When an attempt is made to close an unixFile, if there are
52727 ** other unixFile open on the same inode that are holding locks, the call
52728 ** to close() the file descriptor is deferred until all of the locks clear.
52729 ** The unixInodeInfo structure keeps a list of file descriptors that need to
52730 ** be closed and that list is walked (and cleared) when the last lock
52731 ** clears.
52732 **
52733 ** Yet another problem:  LinuxThreads do not play well with posix locks.
52734 **
52735 ** Many older versions of linux use the LinuxThreads library which is
52736 ** not posix compliant.  Under LinuxThreads, a lock created by thread
52737 ** A cannot be modified or overridden by a different thread B.
52738 ** Only thread A can modify the lock.  Locking behavior is correct
52739 ** if the appliation uses the newer Native Posix Thread Library (NPTL)
52740 ** on linux - with NPTL a lock created by thread A can override locks
52741 ** in thread B.  But there is no way to know at compile-time which
52742 ** threading library is being used.  So there is no way to know at
52743 ** compile-time whether or not thread A can override locks on thread B.
52744 ** One has to do a run-time check to discover the behavior of the
52745 ** current process.
52746 **
52747 */
52748 
52749 /*
52750 ** An instance of the following structure serves as the key used
52751 ** to locate a particular unixInodeInfo object.
52752 */
52753 struct unixFileId {
52754   dev_t dev;                  /* Device number */
52755   ino_t ino;                  /* Inode number */
52756 };
52757 /*
52758 ** An instance of the following structure is allocated for each open
52759 ** inode.  Or, on LinuxThreads, there is one of these structures for
52760 ** each inode opened by each thread.
52761 **
52762 ** A single inode can have multiple file descriptors, so each unixFile
52763 ** structure contains a pointer to an instance of this object and this
52764 ** object keeps a count of the number of unixFile pointing to it.
52765 */
52766 struct unixInodeInfo {
52767   struct unixFileId fileId;       /* The lookup key */
52768   int nShared;                    /* Number of SHARED locks held */
52769   int eFileLock;                  /* One of SHARED_LOCK, RESERVED_LOCK etc. */
52770   int nRef;                       /* Number of pointers to this structure */
52771   int nLock;                      /* Number of outstanding file locks */
52772   UnixUnusedFd *pUnused;          /* Unused file descriptors to close */
52773   unixInodeInfo *pNext;           /* List of all unixInodeInfo objects */
52774   unixInodeInfo *pPrev;           /*    .... doubly linked */
52775 };
52776 
52777 static unixInodeInfo *inodeList = 0;
52778 /*
52779  * Local memory allocation stuff.
52780  */
unqlite_malloc(sxu32 nByte)52781 static void * unqlite_malloc(sxu32 nByte)
52782 {
52783 	SyMemBackend *pAlloc;
52784 	void *p;
52785 	pAlloc = (SyMemBackend *)unqliteExportMemBackend();
52786 	p = SyMemBackendAlloc(pAlloc,nByte);
52787 	return p;
52788 }
unqlite_free(void * p)52789 static void unqlite_free(void *p)
52790 {
52791 	SyMemBackend *pAlloc;
52792 	pAlloc = (SyMemBackend *)unqliteExportMemBackend();
52793 	SyMemBackendFree(pAlloc,p);
52794 }
52795 /*
52796 ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
52797 ** If all such file descriptors are closed without error, the list is
52798 ** cleared and UNQLITE_OK returned.
52799 **
52800 ** Otherwise, if an error occurs, then successfully closed file descriptor
52801 ** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned.
52802 ** not deleted and UNQLITE_IOERR_CLOSE returned.
52803 */
closePendingFds(unixFile * pFile)52804 static int closePendingFds(unixFile *pFile){
52805   int rc = UNQLITE_OK;
52806   unixInodeInfo *pInode = pFile->pInode;
52807   UnixUnusedFd *pError = 0;
52808   UnixUnusedFd *p;
52809   UnixUnusedFd *pNext;
52810   for(p=pInode->pUnused; p; p=pNext){
52811     pNext = p->pNext;
52812     if( close(p->fd) ){
52813       pFile->lastErrno = errno;
52814 	  rc = UNQLITE_IOERR;
52815       p->pNext = pError;
52816       pError = p;
52817     }else{
52818       unqlite_free(p);
52819     }
52820   }
52821   pInode->pUnused = pError;
52822   return rc;
52823 }
52824 /*
52825 ** Release a unixInodeInfo structure previously allocated by findInodeInfo().
52826 **
52827 ** The mutex entered using the unixEnterMutex() function must be held
52828 ** when this function is called.
52829 */
releaseInodeInfo(unixFile * pFile)52830 static void releaseInodeInfo(unixFile *pFile){
52831   unixInodeInfo *pInode = pFile->pInode;
52832   if( pInode ){
52833     pInode->nRef--;
52834     if( pInode->nRef==0 ){
52835       closePendingFds(pFile);
52836       if( pInode->pPrev ){
52837         pInode->pPrev->pNext = pInode->pNext;
52838       }else{
52839         inodeList = pInode->pNext;
52840       }
52841       if( pInode->pNext ){
52842         pInode->pNext->pPrev = pInode->pPrev;
52843       }
52844       unqlite_free(pInode);
52845     }
52846   }
52847 }
52848 /*
52849 ** Given a file descriptor, locate the unixInodeInfo object that
52850 ** describes that file descriptor.  Create a new one if necessary.  The
52851 ** return value might be uninitialized if an error occurs.
52852 **
52853 ** The mutex entered using the unixEnterMutex() function must be held
52854 ** when this function is called.
52855 **
52856 ** Return an appropriate error code.
52857 */
findInodeInfo(unixFile * pFile,unixInodeInfo ** ppInode)52858 static int findInodeInfo(
52859   unixFile *pFile,               /* Unix file with file desc used in the key */
52860   unixInodeInfo **ppInode        /* Return the unixInodeInfo object here */
52861 ){
52862   int rc;                        /* System call return code */
52863   int fd;                        /* The file descriptor for pFile */
52864   struct unixFileId fileId;      /* Lookup key for the unixInodeInfo */
52865   struct stat statbuf;           /* Low-level file information */
52866   unixInodeInfo *pInode = 0;     /* Candidate unixInodeInfo object */
52867 
52868   /* Get low-level information about the file that we can used to
52869   ** create a unique name for the file.
52870   */
52871   fd = pFile->h;
52872   rc = fstat(fd, &statbuf);
52873   if( rc!=0 ){
52874     pFile->lastErrno = errno;
52875 #ifdef EOVERFLOW
52876 	if( pFile->lastErrno==EOVERFLOW ) return UNQLITE_NOTIMPLEMENTED;
52877 #endif
52878     return UNQLITE_IOERR;
52879   }
52880 
52881 #ifdef __APPLE__
52882   /* On OS X on an msdos filesystem, the inode number is reported
52883   ** incorrectly for zero-size files.  See ticket #3260.  To work
52884   ** around this problem (we consider it a bug in OS X, not SQLite)
52885   ** we always increase the file size to 1 by writing a single byte
52886   ** prior to accessing the inode number.  The one byte written is
52887   ** an ASCII 'S' character which also happens to be the first byte
52888   ** in the header of every SQLite database.  In this way, if there
52889   ** is a race condition such that another thread has already populated
52890   ** the first page of the database, no damage is done.
52891   */
52892   if( statbuf.st_size==0 && (pFile->fsFlags & UNQLITE_FSFLAGS_IS_MSDOS)!=0 ){
52893     rc = write(fd, "S", 1);
52894     if( rc!=1 ){
52895       pFile->lastErrno = errno;
52896       return UNQLITE_IOERR;
52897     }
52898     rc = fstat(fd, &statbuf);
52899     if( rc!=0 ){
52900       pFile->lastErrno = errno;
52901       return UNQLITE_IOERR;
52902     }
52903   }
52904 #endif
52905   SyZero(&fileId,sizeof(fileId));
52906   fileId.dev = statbuf.st_dev;
52907   fileId.ino = statbuf.st_ino;
52908   pInode = inodeList;
52909   while( pInode && SyMemcmp((const void *)&fileId,(const void *)&pInode->fileId, sizeof(fileId)) ){
52910     pInode = pInode->pNext;
52911   }
52912   if( pInode==0 ){
52913     pInode = (unixInodeInfo *)unqlite_malloc( sizeof(*pInode) );
52914     if( pInode==0 ){
52915       return UNQLITE_NOMEM;
52916     }
52917     SyZero(pInode,sizeof(*pInode));
52918 	SyMemcpy((const void *)&fileId,(void *)&pInode->fileId,sizeof(fileId));
52919     pInode->nRef = 1;
52920     pInode->pNext = inodeList;
52921     pInode->pPrev = 0;
52922     if( inodeList ) inodeList->pPrev = pInode;
52923     inodeList = pInode;
52924   }else{
52925     pInode->nRef++;
52926   }
52927   *ppInode = pInode;
52928   return UNQLITE_OK;
52929 }
52930 /*
52931 ** This routine checks if there is a RESERVED lock held on the specified
52932 ** file by this or any other process. If such a lock is held, set *pResOut
52933 ** to a non-zero value otherwise *pResOut is set to zero.  The return value
52934 ** is set to UNQLITE_OK unless an I/O error occurs during lock checking.
52935 */
unixCheckReservedLock(unqlite_file * id,int * pResOut)52936 static int unixCheckReservedLock(unqlite_file *id, int *pResOut){
52937   int rc = UNQLITE_OK;
52938   int reserved = 0;
52939   unixFile *pFile = (unixFile*)id;
52940 
52941 
52942   unixEnterMutex(); /* Because pFile->pInode is shared across threads */
52943 
52944   /* Check if a thread in this process holds such a lock */
52945   if( pFile->pInode->eFileLock>SHARED_LOCK ){
52946     reserved = 1;
52947   }
52948 
52949   /* Otherwise see if some other process holds it.
52950   */
52951   if( !reserved ){
52952     struct flock lock;
52953     lock.l_whence = SEEK_SET;
52954     lock.l_start = RESERVED_BYTE;
52955     lock.l_len = 1;
52956     lock.l_type = F_WRLCK;
52957     if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
52958       int tErrno = errno;
52959 	  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
52960       pFile->lastErrno = tErrno;
52961     } else if( lock.l_type!=F_UNLCK ){
52962       reserved = 1;
52963     }
52964   }
52965 
52966   unixLeaveMutex();
52967 
52968   *pResOut = reserved;
52969   return rc;
52970 }
52971 /*
52972 ** Lock the file with the lock specified by parameter eFileLock - one
52973 ** of the following:
52974 **
52975 **     (1) SHARED_LOCK
52976 **     (2) RESERVED_LOCK
52977 **     (3) PENDING_LOCK
52978 **     (4) EXCLUSIVE_LOCK
52979 **
52980 ** Sometimes when requesting one lock state, additional lock states
52981 ** are inserted in between.  The locking might fail on one of the later
52982 ** transitions leaving the lock state different from what it started but
52983 ** still short of its goal.  The following chart shows the allowed
52984 ** transitions and the inserted intermediate states:
52985 **
52986 **    UNLOCKED -> SHARED
52987 **    SHARED -> RESERVED
52988 **    SHARED -> (PENDING) -> EXCLUSIVE
52989 **    RESERVED -> (PENDING) -> EXCLUSIVE
52990 **    PENDING -> EXCLUSIVE
52991 **
52992 ** This routine will only increase a lock.  Use the unqliteOsUnlock()
52993 ** routine to lower a locking level.
52994 */
unixLock(unqlite_file * id,int eFileLock)52995 static int unixLock(unqlite_file *id, int eFileLock){
52996   /* The following describes the implementation of the various locks and
52997   ** lock transitions in terms of the POSIX advisory shared and exclusive
52998   ** lock primitives (called read-locks and write-locks below, to avoid
52999   ** confusion with SQLite lock names). The algorithms are complicated
53000   ** slightly in order to be compatible with unixdows systems simultaneously
53001   ** accessing the same database file, in case that is ever required.
53002   **
53003   ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
53004   ** byte', each single bytes at well known offsets, and the 'shared byte
53005   ** range', a range of 510 bytes at a well known offset.
53006   **
53007   ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
53008   ** byte'.  If this is successful, a random byte from the 'shared byte
53009   ** range' is read-locked and the lock on the 'pending byte' released.
53010   **
53011   ** A process may only obtain a RESERVED lock after it has a SHARED lock.
53012   ** A RESERVED lock is implemented by grabbing a write-lock on the
53013   ** 'reserved byte'.
53014   **
53015   ** A process may only obtain a PENDING lock after it has obtained a
53016   ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
53017   ** on the 'pending byte'. This ensures that no new SHARED locks can be
53018   ** obtained, but existing SHARED locks are allowed to persist. A process
53019   ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
53020   ** This property is used by the algorithm for rolling back a journal file
53021   ** after a crash.
53022   **
53023   ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
53024   ** implemented by obtaining a write-lock on the entire 'shared byte
53025   ** range'. Since all other locks require a read-lock on one of the bytes
53026   ** within this range, this ensures that no other locks are held on the
53027   ** database.
53028   **
53029   ** The reason a single byte cannot be used instead of the 'shared byte
53030   ** range' is that some versions of unixdows do not support read-locks. By
53031   ** locking a random byte from a range, concurrent SHARED locks may exist
53032   ** even if the locking primitive used is always a write-lock.
53033   */
53034   int rc = UNQLITE_OK;
53035   unixFile *pFile = (unixFile*)id;
53036   unixInodeInfo *pInode = pFile->pInode;
53037   struct flock lock;
53038   int s = 0;
53039   int tErrno = 0;
53040 
53041   /* If there is already a lock of this type or more restrictive on the
53042   ** unixFile, do nothing. Don't use the end_lock: exit path, as
53043   ** unixEnterMutex() hasn't been called yet.
53044   */
53045   if( pFile->eFileLock>=eFileLock ){
53046     return UNQLITE_OK;
53047   }
53048   /* This mutex is needed because pFile->pInode is shared across threads
53049   */
53050   unixEnterMutex();
53051   pInode = pFile->pInode;
53052 
53053   /* If some thread using this PID has a lock via a different unixFile*
53054   ** handle that precludes the requested lock, return BUSY.
53055   */
53056   if( (pFile->eFileLock!=pInode->eFileLock &&
53057           (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
53058   ){
53059     rc = UNQLITE_BUSY;
53060     goto end_lock;
53061   }
53062 
53063   /* If a SHARED lock is requested, and some thread using this PID already
53064   ** has a SHARED or RESERVED lock, then increment reference counts and
53065   ** return UNQLITE_OK.
53066   */
53067   if( eFileLock==SHARED_LOCK &&
53068       (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
53069     pFile->eFileLock = SHARED_LOCK;
53070     pInode->nShared++;
53071     pInode->nLock++;
53072     goto end_lock;
53073   }
53074   /* A PENDING lock is needed before acquiring a SHARED lock and before
53075   ** acquiring an EXCLUSIVE lock.  For the SHARED lock, the PENDING will
53076   ** be released.
53077   */
53078   lock.l_len = 1L;
53079   lock.l_whence = SEEK_SET;
53080   if( eFileLock==SHARED_LOCK
53081       || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
53082   ){
53083     lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
53084     lock.l_start = PENDING_BYTE;
53085     s = fcntl(pFile->h, F_SETLK, &lock);
53086     if( s==(-1) ){
53087       tErrno = errno;
53088       rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53089       if( IS_LOCK_ERROR(rc) ){
53090         pFile->lastErrno = tErrno;
53091       }
53092       goto end_lock;
53093     }
53094   }
53095   /* If control gets to this point, then actually go ahead and make
53096   ** operating system calls for the specified lock.
53097   */
53098   if( eFileLock==SHARED_LOCK ){
53099     /* Now get the read-lock */
53100     lock.l_start = SHARED_FIRST;
53101     lock.l_len = SHARED_SIZE;
53102     if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
53103       tErrno = errno;
53104     }
53105     /* Drop the temporary PENDING lock */
53106     lock.l_start = PENDING_BYTE;
53107     lock.l_len = 1L;
53108     lock.l_type = F_UNLCK;
53109     if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
53110       if( s != -1 ){
53111         /* This could happen with a network mount */
53112         tErrno = errno;
53113         rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53114         if( IS_LOCK_ERROR(rc) ){
53115           pFile->lastErrno = tErrno;
53116         }
53117         goto end_lock;
53118       }
53119     }
53120     if( s==(-1) ){
53121 		rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53122       if( IS_LOCK_ERROR(rc) ){
53123         pFile->lastErrno = tErrno;
53124       }
53125     }else{
53126       pFile->eFileLock = SHARED_LOCK;
53127       pInode->nLock++;
53128       pInode->nShared = 1;
53129     }
53130   }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
53131     /* We are trying for an exclusive lock but another thread in this
53132     ** same process is still holding a shared lock. */
53133     rc = UNQLITE_BUSY;
53134   }else{
53135     /* The request was for a RESERVED or EXCLUSIVE lock.  It is
53136     ** assumed that there is a SHARED or greater lock on the file
53137     ** already.
53138     */
53139     lock.l_type = F_WRLCK;
53140     switch( eFileLock ){
53141       case RESERVED_LOCK:
53142         lock.l_start = RESERVED_BYTE;
53143         break;
53144       case EXCLUSIVE_LOCK:
53145         lock.l_start = SHARED_FIRST;
53146         lock.l_len = SHARED_SIZE;
53147         break;
53148       default:
53149 		  /* Can't happen */
53150         break;
53151     }
53152     s = fcntl(pFile->h, F_SETLK, &lock);
53153     if( s==(-1) ){
53154       tErrno = errno;
53155       rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53156       if( IS_LOCK_ERROR(rc) ){
53157         pFile->lastErrno = tErrno;
53158       }
53159     }
53160   }
53161   if( rc==UNQLITE_OK ){
53162     pFile->eFileLock = eFileLock;
53163     pInode->eFileLock = eFileLock;
53164   }else if( eFileLock==EXCLUSIVE_LOCK ){
53165     pFile->eFileLock = PENDING_LOCK;
53166     pInode->eFileLock = PENDING_LOCK;
53167   }
53168 end_lock:
53169   unixLeaveMutex();
53170   return rc;
53171 }
53172 /*
53173 ** Add the file descriptor used by file handle pFile to the corresponding
53174 ** pUnused list.
53175 */
setPendingFd(unixFile * pFile)53176 static void setPendingFd(unixFile *pFile){
53177   unixInodeInfo *pInode = pFile->pInode;
53178   UnixUnusedFd *p = pFile->pUnused;
53179   p->pNext = pInode->pUnused;
53180   pInode->pUnused = p;
53181   pFile->h = -1;
53182   pFile->pUnused = 0;
53183 }
53184 /*
53185 ** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
53186 ** must be either NO_LOCK or SHARED_LOCK.
53187 **
53188 ** If the locking level of the file descriptor is already at or below
53189 ** the requested locking level, this routine is a no-op.
53190 **
53191 ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
53192 ** the byte range is divided into 2 parts and the first part is unlocked then
53193 ** set to a read lock, then the other part is simply unlocked.  This works
53194 ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
53195 ** remove the write lock on a region when a read lock is set.
53196 */
_posixUnlock(unqlite_file * id,int eFileLock,int handleNFSUnlock)53197 static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){
53198   unixFile *pFile = (unixFile*)id;
53199   unixInodeInfo *pInode;
53200   struct flock lock;
53201   int rc = UNQLITE_OK;
53202   int h;
53203   int tErrno;                      /* Error code from system call errors */
53204 
53205    if( pFile->eFileLock<=eFileLock ){
53206     return UNQLITE_OK;
53207   }
53208   unixEnterMutex();
53209 
53210   h = pFile->h;
53211   pInode = pFile->pInode;
53212 
53213   if( pFile->eFileLock>SHARED_LOCK ){
53214     /* downgrading to a shared lock on NFS involves clearing the write lock
53215     ** before establishing the readlock - to avoid a race condition we downgrade
53216     ** the lock in 2 blocks, so that part of the range will be covered by a
53217     ** write lock until the rest is covered by a read lock:
53218     **  1:   [WWWWW]
53219     **  2:   [....W]
53220     **  3:   [RRRRW]
53221     **  4:   [RRRR.]
53222     */
53223     if( eFileLock==SHARED_LOCK ){
53224       if( handleNFSUnlock ){
53225         off_t divSize = SHARED_SIZE - 1;
53226 
53227         lock.l_type = F_UNLCK;
53228         lock.l_whence = SEEK_SET;
53229         lock.l_start = SHARED_FIRST;
53230         lock.l_len = divSize;
53231         if( fcntl(h, F_SETLK, &lock)==(-1) ){
53232           tErrno = errno;
53233 		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53234           if( IS_LOCK_ERROR(rc) ){
53235             pFile->lastErrno = tErrno;
53236           }
53237           goto end_unlock;
53238         }
53239         lock.l_type = F_RDLCK;
53240         lock.l_whence = SEEK_SET;
53241         lock.l_start = SHARED_FIRST;
53242         lock.l_len = divSize;
53243         if( fcntl(h, F_SETLK, &lock)==(-1) ){
53244           tErrno = errno;
53245 		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53246           if( IS_LOCK_ERROR(rc) ){
53247             pFile->lastErrno = tErrno;
53248           }
53249           goto end_unlock;
53250         }
53251         lock.l_type = F_UNLCK;
53252         lock.l_whence = SEEK_SET;
53253         lock.l_start = SHARED_FIRST+divSize;
53254         lock.l_len = SHARED_SIZE-divSize;
53255         if( fcntl(h, F_SETLK, &lock)==(-1) ){
53256           tErrno = errno;
53257 		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53258           if( IS_LOCK_ERROR(rc) ){
53259             pFile->lastErrno = tErrno;
53260           }
53261           goto end_unlock;
53262         }
53263       }else{
53264         lock.l_type = F_RDLCK;
53265         lock.l_whence = SEEK_SET;
53266         lock.l_start = SHARED_FIRST;
53267         lock.l_len = SHARED_SIZE;
53268         if( fcntl(h, F_SETLK, &lock)==(-1) ){
53269           tErrno = errno;
53270 		  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53271           if( IS_LOCK_ERROR(rc) ){
53272             pFile->lastErrno = tErrno;
53273           }
53274           goto end_unlock;
53275         }
53276       }
53277     }
53278     lock.l_type = F_UNLCK;
53279     lock.l_whence = SEEK_SET;
53280     lock.l_start = PENDING_BYTE;
53281     lock.l_len = 2L;
53282     if( fcntl(h, F_SETLK, &lock)!=(-1) ){
53283       pInode->eFileLock = SHARED_LOCK;
53284     }else{
53285       tErrno = errno;
53286 	  rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53287       if( IS_LOCK_ERROR(rc) ){
53288         pFile->lastErrno = tErrno;
53289       }
53290       goto end_unlock;
53291     }
53292   }
53293   if( eFileLock==NO_LOCK ){
53294     /* Decrement the shared lock counter.  Release the lock using an
53295     ** OS call only when all threads in this same process have released
53296     ** the lock.
53297     */
53298     pInode->nShared--;
53299     if( pInode->nShared==0 ){
53300       lock.l_type = F_UNLCK;
53301       lock.l_whence = SEEK_SET;
53302       lock.l_start = lock.l_len = 0L;
53303 
53304       if( fcntl(h, F_SETLK, &lock)!=(-1) ){
53305         pInode->eFileLock = NO_LOCK;
53306       }else{
53307         tErrno = errno;
53308 		rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
53309         if( IS_LOCK_ERROR(rc) ){
53310           pFile->lastErrno = tErrno;
53311         }
53312         pInode->eFileLock = NO_LOCK;
53313         pFile->eFileLock = NO_LOCK;
53314       }
53315     }
53316 
53317     /* Decrement the count of locks against this same file.  When the
53318     ** count reaches zero, close any other file descriptors whose close
53319     ** was deferred because of outstanding locks.
53320     */
53321     pInode->nLock--;
53322 
53323     if( pInode->nLock==0 ){
53324       int rc2 = closePendingFds(pFile);
53325       if( rc==UNQLITE_OK ){
53326         rc = rc2;
53327       }
53328     }
53329   }
53330 
53331 end_unlock:
53332 
53333   unixLeaveMutex();
53334 
53335   if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock;
53336   return rc;
53337 }
53338 /*
53339 ** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
53340 ** must be either NO_LOCK or SHARED_LOCK.
53341 **
53342 ** If the locking level of the file descriptor is already at or below
53343 ** the requested locking level, this routine is a no-op.
53344 */
unixUnlock(unqlite_file * id,int eFileLock)53345 static int unixUnlock(unqlite_file *id, int eFileLock){
53346   return _posixUnlock(id, eFileLock, 0);
53347 }
53348 /*
53349 ** This function performs the parts of the "close file" operation
53350 ** common to all locking schemes. It closes the directory and file
53351 ** handles, if they are valid, and sets all fields of the unixFile
53352 ** structure to 0.
53353 **
53354 */
closeUnixFile(unqlite_file * id)53355 static int closeUnixFile(unqlite_file *id){
53356   unixFile *pFile = (unixFile*)id;
53357   if( pFile ){
53358     if( pFile->dirfd>=0 ){
53359       int err = close(pFile->dirfd);
53360       if( err ){
53361         pFile->lastErrno = errno;
53362         return UNQLITE_IOERR;
53363       }else{
53364         pFile->dirfd=-1;
53365       }
53366     }
53367     if( pFile->h>=0 ){
53368       int err = close(pFile->h);
53369       if( err ){
53370         pFile->lastErrno = errno;
53371         return UNQLITE_IOERR;
53372       }
53373     }
53374     unqlite_free(pFile->pUnused);
53375     SyZero(pFile,sizeof(unixFile));
53376   }
53377   return UNQLITE_OK;
53378 }
53379 /*
53380 ** Close a file.
53381 */
unixClose(unqlite_file * id)53382 static int unixClose(unqlite_file *id){
53383   int rc = UNQLITE_OK;
53384   if( id ){
53385     unixFile *pFile = (unixFile *)id;
53386     unixUnlock(id, NO_LOCK);
53387     unixEnterMutex();
53388     if( pFile->pInode && pFile->pInode->nLock ){
53389       /* If there are outstanding locks, do not actually close the file just
53390       ** yet because that would clear those locks.  Instead, add the file
53391       ** descriptor to pInode->pUnused list.  It will be automatically closed
53392       ** when the last lock is cleared.
53393       */
53394       setPendingFd(pFile);
53395     }
53396     releaseInodeInfo(pFile);
53397     rc = closeUnixFile(id);
53398     unixLeaveMutex();
53399   }
53400   return rc;
53401 }
53402 /************** End of the posix advisory lock implementation *****************
53403 ******************************************************************************/
53404 /*
53405 **
53406 ** The next division contains implementations for all methods of the
53407 ** unqlite_file object other than the locking methods.  The locking
53408 ** methods were defined in divisions above (one locking method per
53409 ** division).  Those methods that are common to all locking modes
53410 ** are gather together into this division.
53411 */
53412 /*
53413 ** Seek to the offset passed as the second argument, then read cnt
53414 ** bytes into pBuf. Return the number of bytes actually read.
53415 **
53416 ** NB:  If you define USE_PREAD or USE_PREAD64, then it might also
53417 ** be necessary to define _XOPEN_SOURCE to be 500.  This varies from
53418 ** one system to another.  Since SQLite does not define USE_PREAD
53419 ** any form by default, we will not attempt to define _XOPEN_SOURCE.
53420 ** See tickets #2741 and #2681.
53421 **
53422 ** To avoid stomping the errno value on a failed read the lastErrno value
53423 ** is set before returning.
53424 */
seekAndRead(unixFile * id,unqlite_int64 offset,void * pBuf,int cnt)53425 static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){
53426   int got;
53427 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
53428   unqlite_int64 newOffset;
53429 #endif
53430 
53431 #if defined(USE_PREAD)
53432   got = pread(id->h, pBuf, cnt, offset);
53433 #elif defined(USE_PREAD64)
53434   got = pread64(id->h, pBuf, cnt, offset);
53435 #else
53436   newOffset = lseek(id->h, offset, SEEK_SET);
53437 
53438   if( newOffset!=offset ){
53439     if( newOffset == -1 ){
53440       ((unixFile*)id)->lastErrno = errno;
53441     }else{
53442       ((unixFile*)id)->lastErrno = 0;
53443     }
53444     return -1;
53445   }
53446   got = read(id->h, pBuf, cnt);
53447 #endif
53448   if( got<0 ){
53449     ((unixFile*)id)->lastErrno = errno;
53450   }
53451   return got;
53452 }
53453 /*
53454 ** Read data from a file into a buffer.  Return UNQLITE_OK if all
53455 ** bytes were read successfully and UNQLITE_IOERR if anything goes
53456 ** wrong.
53457 */
unixRead(unqlite_file * id,void * pBuf,unqlite_int64 amt,unqlite_int64 offset)53458 static int unixRead(
53459   unqlite_file *id,
53460   void *pBuf,
53461   unqlite_int64 amt,
53462   unqlite_int64 offset
53463 ){
53464   unixFile *pFile = (unixFile *)id;
53465   int got;
53466 
53467   got = seekAndRead(pFile, offset, pBuf, (int)amt);
53468   if( got==(int)amt ){
53469     return UNQLITE_OK;
53470   }else if( got<0 ){
53471     /* lastErrno set by seekAndRead */
53472     return UNQLITE_IOERR;
53473   }else{
53474     pFile->lastErrno = 0; /* not a system error */
53475     /* Unread parts of the buffer must be zero-filled */
53476     SyZero(&((char*)pBuf)[got],(sxu32)amt-got);
53477     return UNQLITE_IOERR;
53478   }
53479 }
53480 /*
53481 ** Seek to the offset in id->offset then read cnt bytes into pBuf.
53482 ** Return the number of bytes actually read.  Update the offset.
53483 **
53484 ** To avoid stomping the errno value on a failed write the lastErrno value
53485 ** is set before returning.
53486 */
seekAndWrite(unixFile * id,unqlite_int64 offset,const void * pBuf,unqlite_int64 cnt)53487 static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, unqlite_int64 cnt){
53488   int got;
53489 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
53490   unqlite_int64 newOffset;
53491 #endif
53492 
53493 #if defined(USE_PREAD)
53494   got = pwrite(id->h, pBuf, cnt, offset);
53495 #elif defined(USE_PREAD64)
53496   got = pwrite64(id->h, pBuf, cnt, offset);
53497 #else
53498   newOffset = lseek(id->h, offset, SEEK_SET);
53499   if( newOffset!=offset ){
53500     if( newOffset == -1 ){
53501       ((unixFile*)id)->lastErrno = errno;
53502     }else{
53503       ((unixFile*)id)->lastErrno = 0;
53504     }
53505     return -1;
53506   }
53507   got = write(id->h, pBuf, cnt);
53508 #endif
53509   if( got<0 ){
53510     ((unixFile*)id)->lastErrno = errno;
53511   }
53512   return got;
53513 }
53514 /*
53515 ** Write data from a buffer into a file.  Return UNQLITE_OK on success
53516 ** or some other error code on failure.
53517 */
unixWrite(unqlite_file * id,const void * pBuf,unqlite_int64 amt,unqlite_int64 offset)53518 static int unixWrite(
53519   unqlite_file *id,
53520   const void *pBuf,
53521   unqlite_int64 amt,
53522   unqlite_int64 offset
53523 ){
53524   unixFile *pFile = (unixFile*)id;
53525   int wrote = 0;
53526 
53527   while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
53528     amt -= wrote;
53529     offset += wrote;
53530     pBuf = &((char*)pBuf)[wrote];
53531   }
53532 
53533   if( amt>0 ){
53534     if( wrote<0 ){
53535       /* lastErrno set by seekAndWrite */
53536       return UNQLITE_IOERR;
53537     }else{
53538       pFile->lastErrno = 0; /* not a system error */
53539       return UNQLITE_FULL;
53540     }
53541   }
53542   return UNQLITE_OK;
53543 }
53544 /*
53545 ** We do not trust systems to provide a working fdatasync().  Some do.
53546 ** Others do no.  To be safe, we will stick with the (slower) fsync().
53547 ** If you know that your system does support fdatasync() correctly,
53548 ** then simply compile with -Dfdatasync=fdatasync
53549 */
53550 #if !defined(fdatasync) && !defined(__linux__)
53551 # define fdatasync fsync
53552 #endif
53553 
53554 /*
53555 ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
53556 ** the F_FULLFSYNC macro is defined.  F_FULLFSYNC is currently
53557 ** only available on Mac OS X.  But that could change.
53558 */
53559 #ifdef F_FULLFSYNC
53560 # define HAVE_FULLFSYNC 1
53561 #else
53562 # define HAVE_FULLFSYNC 0
53563 #endif
53564 /*
53565 ** The fsync() system call does not work as advertised on many
53566 ** unix systems.  The following procedure is an attempt to make
53567 ** it work better.
53568 **
53569 **
53570 ** SQLite sets the dataOnly flag if the size of the file is unchanged.
53571 ** The idea behind dataOnly is that it should only write the file content
53572 ** to disk, not the inode.  We only set dataOnly if the file size is
53573 ** unchanged since the file size is part of the inode.  However,
53574 ** Ted Ts'o tells us that fdatasync() will also write the inode if the
53575 ** file size has changed.  The only real difference between fdatasync()
53576 ** and fsync(), Ted tells us, is that fdatasync() will not flush the
53577 ** inode if the mtime or owner or other inode attributes have changed.
53578 ** We only care about the file size, not the other file attributes, so
53579 ** as far as SQLite is concerned, an fdatasync() is always adequate.
53580 ** So, we always use fdatasync() if it is available, regardless of
53581 ** the value of the dataOnly flag.
53582 */
full_fsync(int fd,int fullSync,int dataOnly)53583 static int full_fsync(int fd, int fullSync, int dataOnly){
53584   int rc;
53585 #if HAVE_FULLFSYNC
53586   SXUNUSED(dataOnly);
53587 #else
53588   SXUNUSED(fullSync);
53589   SXUNUSED(dataOnly);
53590 #endif
53591 
53592   /* If we compiled with the UNQLITE_NO_SYNC flag, then syncing is a
53593   ** no-op
53594   */
53595 #if HAVE_FULLFSYNC
53596   if( fullSync ){
53597     rc = fcntl(fd, F_FULLFSYNC, 0);
53598   }else{
53599     rc = 1;
53600   }
53601   /* If the FULLFSYNC failed, fall back to attempting an fsync().
53602   ** It shouldn't be possible for fullfsync to fail on the local
53603   ** file system (on OSX), so failure indicates that FULLFSYNC
53604   ** isn't supported for this file system. So, attempt an fsync
53605   ** and (for now) ignore the overhead of a superfluous fcntl call.
53606   ** It'd be better to detect fullfsync support once and avoid
53607   ** the fcntl call every time sync is called.
53608   */
53609   if( rc ) rc = fsync(fd);
53610 
53611 #elif defined(__APPLE__)
53612   /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
53613   ** so currently we default to the macro that redefines fdatasync to fsync
53614   */
53615   rc = fsync(fd);
53616 #else
53617   rc = fdatasync(fd);
53618 #endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */
53619   if( rc!= -1 ){
53620     rc = 0;
53621   }
53622   return rc;
53623 }
53624 /*
53625 ** Make sure all writes to a particular file are committed to disk.
53626 **
53627 ** If dataOnly==0 then both the file itself and its metadata (file
53628 ** size, access time, etc) are synced.  If dataOnly!=0 then only the
53629 ** file data is synced.
53630 **
53631 ** Under Unix, also make sure that the directory entry for the file
53632 ** has been created by fsync-ing the directory that contains the file.
53633 ** If we do not do this and we encounter a power failure, the directory
53634 ** entry for the journal might not exist after we reboot.  The next
53635 ** SQLite to access the file will not know that the journal exists (because
53636 ** the directory entry for the journal was never created) and the transaction
53637 ** will not roll back - possibly leading to database corruption.
53638 */
unixSync(unqlite_file * id,int flags)53639 static int unixSync(unqlite_file *id, int flags){
53640   int rc;
53641   unixFile *pFile = (unixFile*)id;
53642 
53643   int isDataOnly = (flags&UNQLITE_SYNC_DATAONLY);
53644   int isFullsync = (flags&0x0F)==UNQLITE_SYNC_FULL;
53645 
53646   rc = full_fsync(pFile->h, isFullsync, isDataOnly);
53647 
53648   if( rc ){
53649     pFile->lastErrno = errno;
53650     return UNQLITE_IOERR;
53651   }
53652   if( pFile->dirfd>=0 ){
53653     int err;
53654 #ifndef UNQLITE_DISABLE_DIRSYNC
53655     /* The directory sync is only attempted if full_fsync is
53656     ** turned off or unavailable.  If a full_fsync occurred above,
53657     ** then the directory sync is superfluous.
53658     */
53659     if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
53660        /*
53661        ** We have received multiple reports of fsync() returning
53662        ** errors when applied to directories on certain file systems.
53663        ** A failed directory sync is not a big deal.  So it seems
53664        ** better to ignore the error.  Ticket #1657
53665        */
53666        /* pFile->lastErrno = errno; */
53667        /* return UNQLITE_IOERR; */
53668     }
53669 #endif
53670     err = close(pFile->dirfd); /* Only need to sync once, so close the */
53671     if( err==0 ){              /* directory when we are done */
53672       pFile->dirfd = -1;
53673     }else{
53674       pFile->lastErrno = errno;
53675       rc = UNQLITE_IOERR;
53676     }
53677   }
53678   return rc;
53679 }
53680 /*
53681 ** Truncate an open file to a specified size
53682 */
unixTruncate(unqlite_file * id,sxi64 nByte)53683 static int unixTruncate(unqlite_file *id, sxi64 nByte){
53684   unixFile *pFile = (unixFile *)id;
53685   int rc;
53686 
53687   rc = ftruncate(pFile->h, (off_t)nByte);
53688   if( rc ){
53689     pFile->lastErrno = errno;
53690     return UNQLITE_IOERR;
53691   }else{
53692     return UNQLITE_OK;
53693   }
53694 }
53695 /*
53696 ** Determine the current size of a file in bytes
53697 */
unixFileSize(unqlite_file * id,sxi64 * pSize)53698 static int unixFileSize(unqlite_file *id,sxi64 *pSize){
53699   int rc;
53700   struct stat buf;
53701 
53702   rc = fstat(((unixFile*)id)->h, &buf);
53703 
53704   if( rc!=0 ){
53705     ((unixFile*)id)->lastErrno = errno;
53706     return UNQLITE_IOERR;
53707   }
53708   *pSize = buf.st_size;
53709 
53710   /* When opening a zero-size database, the findInodeInfo() procedure
53711   ** writes a single byte into that file in order to work around a bug
53712   ** in the OS-X msdos filesystem.  In order to avoid problems with upper
53713   ** layers, we need to report this file size as zero even though it is
53714   ** really 1.   Ticket #3260.
53715   */
53716   if( *pSize==1 ) *pSize = 0;
53717 
53718   return UNQLITE_OK;
53719 }
53720 /*
53721 ** Return the sector size in bytes of the underlying block device for
53722 ** the specified file. This is almost always 512 bytes, but may be
53723 ** larger for some devices.
53724 **
53725 ** SQLite code assumes this function cannot fail. It also assumes that
53726 ** if two files are created in the same file-system directory (i.e.
53727 ** a database and its journal file) that the sector size will be the
53728 ** same for both.
53729 */
unixSectorSize(unqlite_file * NotUsed)53730 static int unixSectorSize(unqlite_file *NotUsed){
53731   SXUNUSED(NotUsed);
53732   return UNQLITE_DEFAULT_SECTOR_SIZE;
53733 }
53734 /*
53735 ** This vector defines all the methods that can operate on an
53736 ** unqlite_file for Windows systems.
53737 */
53738 static const unqlite_io_methods unixIoMethod = {
53739   1,                              /* iVersion */
53740   unixClose,                       /* xClose */
53741   unixRead,                        /* xRead */
53742   unixWrite,                       /* xWrite */
53743   unixTruncate,                    /* xTruncate */
53744   unixSync,                        /* xSync */
53745   unixFileSize,                    /* xFileSize */
53746   unixLock,                        /* xLock */
53747   unixUnlock,                      /* xUnlock */
53748   unixCheckReservedLock,           /* xCheckReservedLock */
53749   unixSectorSize,                  /* xSectorSize */
53750 };
53751 /****************************************************************************
53752 **************************** unqlite_vfs methods ****************************
53753 **
53754 ** This division contains the implementation of methods on the
53755 ** unqlite_vfs object.
53756 */
53757 /*
53758 ** Initialize the contents of the unixFile structure pointed to by pId.
53759 */
fillInUnixFile(unqlite_vfs * pVfs,int h,int dirfd,unqlite_file * pId,const char * zFilename,int noLock,int isDelete)53760 static int fillInUnixFile(
53761   unqlite_vfs *pVfs,      /* Pointer to vfs object */
53762   int h,                  /* Open file descriptor of file being opened */
53763   int dirfd,              /* Directory file descriptor */
53764   unqlite_file *pId,      /* Write to the unixFile structure here */
53765   const char *zFilename,  /* Name of the file being opened */
53766   int noLock,             /* Omit locking if true */
53767   int isDelete            /* Delete on close if true */
53768 ){
53769   const unqlite_io_methods *pLockingStyle = &unixIoMethod;
53770   unixFile *pNew = (unixFile *)pId;
53771   int rc = UNQLITE_OK;
53772 
53773   /* Parameter isDelete is only used on vxworks. Express this explicitly
53774   ** here to prevent compiler warnings about unused parameters.
53775   */
53776   SXUNUSED(isDelete);
53777   SXUNUSED(noLock);
53778   SXUNUSED(pVfs);
53779 
53780   pNew->h = h;
53781   pNew->dirfd = dirfd;
53782   pNew->fileFlags = 0;
53783   pNew->zPath = zFilename;
53784 
53785   unixEnterMutex();
53786   rc = findInodeInfo(pNew, &pNew->pInode);
53787   if( rc!=UNQLITE_OK ){
53788       /* If an error occured in findInodeInfo(), close the file descriptor
53789       ** immediately, before releasing the mutex. findInodeInfo() may fail
53790       ** in two scenarios:
53791       **
53792       **   (a) A call to fstat() failed.
53793       **   (b) A malloc failed.
53794       **
53795       ** Scenario (b) may only occur if the process is holding no other
53796       ** file descriptors open on the same file. If there were other file
53797       ** descriptors on this file, then no malloc would be required by
53798       ** findInodeInfo(). If this is the case, it is quite safe to close
53799       ** handle h - as it is guaranteed that no posix locks will be released
53800       ** by doing so.
53801       **
53802       ** If scenario (a) caused the error then things are not so safe. The
53803       ** implicit assumption here is that if fstat() fails, things are in
53804       ** such bad shape that dropping a lock or two doesn't matter much.
53805       */
53806       close(h);
53807       h = -1;
53808   }
53809   unixLeaveMutex();
53810 
53811   pNew->lastErrno = 0;
53812   if( rc!=UNQLITE_OK ){
53813     if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */
53814     if( h>=0 ) close(h);
53815   }else{
53816     pNew->pMethod = pLockingStyle;
53817   }
53818   return rc;
53819 }
53820 /*
53821 ** Open a file descriptor to the directory containing file zFilename.
53822 ** If successful, *pFd is set to the opened file descriptor and
53823 ** UNQLITE_OK is returned. If an error occurs, either UNQLITE_NOMEM
53824 ** or UNQLITE_CANTOPEN is returned and *pFd is set to an undefined
53825 ** value.
53826 **
53827 ** If UNQLITE_OK is returned, the caller is responsible for closing
53828 ** the file descriptor *pFd using close().
53829 */
openDirectory(const char * zFilename,int * pFd)53830 static int openDirectory(const char *zFilename, int *pFd){
53831   sxu32 ii;
53832   int fd = -1;
53833   char zDirname[MAX_PATHNAME+1];
53834   sxu32 n;
53835   n = Systrcpy(zDirname,sizeof(zDirname),zFilename,0);
53836   for(ii=n; ii>1 && zDirname[ii]!='/'; ii--);
53837   if( ii>0 ){
53838     zDirname[ii] = '\0';
53839     fd = open(zDirname, O_RDONLY|O_BINARY, 0);
53840     if( fd>=0 ){
53841 #ifdef FD_CLOEXEC
53842       fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
53843 #endif
53844     }
53845   }
53846   *pFd = fd;
53847   return (fd>=0?UNQLITE_OK: UNQLITE_IOERR );
53848 }
53849 /*
53850 ** Search for an unused file descriptor that was opened on the database
53851 ** file (not a journal or master-journal file) identified by pathname
53852 ** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second
53853 ** argument to this function.
53854 **
53855 ** Such a file descriptor may exist if a database connection was closed
53856 ** but the associated file descriptor could not be closed because some
53857 ** other file descriptor open on the same file is holding a file-lock.
53858 ** Refer to comments in the unixClose() function and the lengthy comment
53859 ** describing "Posix Advisory Locking" at the start of this file for
53860 ** further details. Also, ticket #4018.
53861 **
53862 ** If a suitable file descriptor is found, then it is returned. If no
53863 ** such file descriptor is located, -1 is returned.
53864 */
findReusableFd(const char * zPath,int flags)53865 static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
53866   UnixUnusedFd *pUnused = 0;
53867   struct stat sStat;                   /* Results of stat() call */
53868   /* A stat() call may fail for various reasons. If this happens, it is
53869   ** almost certain that an open() call on the same path will also fail.
53870   ** For this reason, if an error occurs in the stat() call here, it is
53871   ** ignored and -1 is returned. The caller will try to open a new file
53872   ** descriptor on the same path, fail, and return an error to SQLite.
53873   **
53874   ** Even if a subsequent open() call does succeed, the consequences of
53875   ** not searching for a resusable file descriptor are not dire.  */
53876   if( 0==stat(zPath, &sStat) ){
53877     unixInodeInfo *pInode;
53878 
53879     unixEnterMutex();
53880     pInode = inodeList;
53881     while( pInode && (pInode->fileId.dev!=sStat.st_dev
53882                      || pInode->fileId.ino!=sStat.st_ino) ){
53883        pInode = pInode->pNext;
53884     }
53885     if( pInode ){
53886       UnixUnusedFd **pp;
53887       for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
53888       pUnused = *pp;
53889       if( pUnused ){
53890         *pp = pUnused->pNext;
53891       }
53892     }
53893     unixLeaveMutex();
53894   }
53895   return pUnused;
53896 }
53897 /*
53898 ** This function is called by unixOpen() to determine the unix permissions
53899 ** to create new files with. If no error occurs, then UNQLITE_OK is returned
53900 ** and a value suitable for passing as the third argument to open(2) is
53901 ** written to *pMode. If an IO error occurs, an SQLite error code is
53902 ** returned and the value of *pMode is not modified.
53903 **
53904 ** If the file being opened is a temporary file, it is always created with
53905 ** the octal permissions 0600 (read/writable by owner only). If the file
53906 ** is a database or master journal file, it is created with the permissions
53907 ** mask UNQLITE_DEFAULT_FILE_PERMISSIONS.
53908 **
53909 ** Finally, if the file being opened is a WAL or regular journal file, then
53910 ** this function queries the file-system for the permissions on the
53911 ** corresponding database file and sets *pMode to this value. Whenever
53912 ** possible, WAL and journal files are created using the same permissions
53913 ** as the associated database file.
53914 */
findCreateFileMode(const char * zPath,int flags,mode_t * pMode)53915 static int findCreateFileMode(
53916   const char *zPath,              /* Path of file (possibly) being created */
53917   int flags,                      /* Flags passed as 4th argument to xOpen() */
53918   mode_t *pMode                   /* OUT: Permissions to open file with */
53919 ){
53920   int rc = UNQLITE_OK;             /* Return Code */
53921   if( flags & UNQLITE_OPEN_TEMP_DB ){
53922     *pMode = 0600;
53923      SXUNUSED(zPath);
53924   }else{
53925     *pMode = UNQLITE_DEFAULT_FILE_PERMISSIONS;
53926   }
53927   return rc;
53928 }
53929 /*
53930 ** Open the file zPath.
53931 **
53932 ** Previously, the SQLite OS layer used three functions in place of this
53933 ** one:
53934 **
53935 **     unqliteOsOpenReadWrite();
53936 **     unqliteOsOpenReadOnly();
53937 **     unqliteOsOpenExclusive();
53938 **
53939 ** These calls correspond to the following combinations of flags:
53940 **
53941 **     ReadWrite() ->     (READWRITE | CREATE)
53942 **     ReadOnly()  ->     (READONLY)
53943 **     OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
53944 **
53945 ** The old OpenExclusive() accepted a boolean argument - "delFlag". If
53946 ** true, the file was configured to be automatically deleted when the
53947 ** file handle closed. To achieve the same effect using this new
53948 ** interface, add the DELETEONCLOSE flag to those specified above for
53949 ** OpenExclusive().
53950 */
unixOpen(unqlite_vfs * pVfs,const char * zPath,unqlite_file * pFile,unsigned int flags)53951 static int unixOpen(
53952   unqlite_vfs *pVfs,           /* The VFS for which this is the xOpen method */
53953   const char *zPath,           /* Pathname of file to be opened */
53954   unqlite_file *pFile,         /* The file descriptor to be filled in */
53955   unsigned int flags           /* Input flags to control the opening */
53956 ){
53957   unixFile *p = (unixFile *)pFile;
53958   int fd = -1;                   /* File descriptor returned by open() */
53959   int dirfd = -1;                /* Directory file descriptor */
53960   int openFlags = 0;             /* Flags to pass to open() */
53961   int noLock;                    /* True to omit locking primitives */
53962   int rc = UNQLITE_OK;            /* Function Return Code */
53963   UnixUnusedFd *pUnused;
53964   int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
53965   int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
53966   int isCreate     = (flags & UNQLITE_OPEN_CREATE);
53967   int isReadonly   = (flags & UNQLITE_OPEN_READONLY);
53968   int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
53969   /* If creating a master or main-file journal, this function will open
53970   ** a file-descriptor on the directory too. The first time unixSync()
53971   ** is called the directory file descriptor will be fsync()ed and close()d.
53972   */
53973   int isOpenDirectory = isCreate ;
53974   const char *zName = zPath;
53975 
53976   SyZero(p,sizeof(unixFile));
53977 
53978   pUnused = findReusableFd(zName, flags);
53979   if( pUnused ){
53980 	  fd = pUnused->fd;
53981   }else{
53982 	  pUnused = unqlite_malloc(sizeof(*pUnused));
53983       if( !pUnused ){
53984         return UNQLITE_NOMEM;
53985       }
53986   }
53987   p->pUnused = pUnused;
53988 
53989   /* Determine the value of the flags parameter passed to POSIX function
53990   ** open(). These must be calculated even if open() is not called, as
53991   ** they may be stored as part of the file handle and used by the
53992   ** 'conch file' locking functions later on.  */
53993   if( isReadonly )  openFlags |= O_RDONLY;
53994   if( isReadWrite ) openFlags |= O_RDWR;
53995   if( isCreate )    openFlags |= O_CREAT;
53996   if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
53997   openFlags |= (O_LARGEFILE|O_BINARY);
53998 
53999   if( fd<0 ){
54000     mode_t openMode;              /* Permissions to create file with */
54001     rc = findCreateFileMode(zName, flags, &openMode);
54002     if( rc!=UNQLITE_OK ){
54003       return rc;
54004     }
54005     fd = open(zName, openFlags, openMode);
54006     if( fd<0 ){
54007 	  rc = UNQLITE_IOERR;
54008       goto open_finished;
54009     }
54010   }
54011 
54012   if( p->pUnused ){
54013     p->pUnused->fd = fd;
54014     p->pUnused->flags = flags;
54015   }
54016 
54017   if( isDelete ){
54018     unlink(zName);
54019   }
54020 
54021   if( isOpenDirectory ){
54022     rc = openDirectory(zPath, &dirfd);
54023     if( rc!=UNQLITE_OK ){
54024       /* It is safe to close fd at this point, because it is guaranteed not
54025       ** to be open on a database file. If it were open on a database file,
54026       ** it would not be safe to close as this would release any locks held
54027       ** on the file by this process.  */
54028       close(fd);             /* silently leak if fail, already in error */
54029       goto open_finished;
54030     }
54031   }
54032 
54033 #ifdef FD_CLOEXEC
54034   fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
54035 #endif
54036 
54037   noLock = 0;
54038 
54039 #if defined(__APPLE__)
54040   struct statfs fsInfo;
54041   if( fstatfs(fd, &fsInfo) == -1 ){
54042     ((unixFile*)pFile)->lastErrno = errno;
54043     if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
54044     close(fd); /* silently leak if fail, in error */
54045     return UNQLITE_IOERR;
54046   }
54047   if (0 == SyStrncmp("msdos", fsInfo.f_fstypename, 5)) {
54048     ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS;
54049   }
54050 #endif
54051 
54052   rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
54053 open_finished:
54054   if( rc!=UNQLITE_OK ){
54055     unqlite_free(p->pUnused);
54056   }
54057   return rc;
54058 }
54059 /*
54060 ** Delete the file at zPath. If the dirSync argument is true, fsync()
54061 ** the directory after deleting the file.
54062 */
unixDelete(unqlite_vfs * NotUsed,const char * zPath,int dirSync)54063 static int unixDelete(
54064   unqlite_vfs *NotUsed,     /* VFS containing this as the xDelete method */
54065   const char *zPath,        /* Name of file to be deleted */
54066   int dirSync               /* If true, fsync() directory after deleting file */
54067 ){
54068   int rc = UNQLITE_OK;
54069   SXUNUSED(NotUsed);
54070 
54071   if( unlink(zPath)==(-1) && errno!=ENOENT ){
54072 	  return UNQLITE_IOERR;
54073   }
54074 #ifndef UNQLITE_DISABLE_DIRSYNC
54075   if( dirSync ){
54076     int fd;
54077     rc = openDirectory(zPath, &fd);
54078     if( rc==UNQLITE_OK ){
54079       if( fsync(fd) )
54080       {
54081         rc = UNQLITE_IOERR;
54082       }
54083       if( close(fd) && !rc ){
54084         rc = UNQLITE_IOERR;
54085       }
54086     }
54087   }
54088 #endif
54089   return rc;
54090 }
54091 /*
54092 ** Sleep for a little while.  Return the amount of time slept.
54093 ** The argument is the number of microseconds we want to sleep.
54094 ** The return value is the number of microseconds of sleep actually
54095 ** requested from the underlying operating system, a number which
54096 ** might be greater than or equal to the argument, but not less
54097 ** than the argument.
54098 */
unixSleep(unqlite_vfs * NotUsed,int microseconds)54099 static int unixSleep(unqlite_vfs *NotUsed, int microseconds)
54100 {
54101 #if defined(HAVE_USLEEP) && HAVE_USLEEP
54102   usleep(microseconds);
54103   SXUNUSED(NotUsed);
54104   return microseconds;
54105 #else
54106   int seconds = (microseconds+999999)/1000000;
54107   SXUNUSED(NotUsed);
54108   sleep(seconds);
54109   return seconds*1000000;
54110 #endif
54111 }
54112 /*
54113  * Export the current system time.
54114  */
unixCurrentTime(unqlite_vfs * pVfs,Sytm * pOut)54115 static int unixCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
54116 {
54117 	struct tm *pTm;
54118 	time_t tt;
54119 	SXUNUSED(pVfs);
54120 	time(&tt);
54121 	pTm = gmtime(&tt);
54122 	if( pTm ){ /* Yes, it can fail */
54123 		STRUCT_TM_TO_SYTM(pTm,pOut);
54124 	}
54125 	return UNQLITE_OK;
54126 }
54127 /*
54128 ** Test the existance of or access permissions of file zPath. The
54129 ** test performed depends on the value of flags:
54130 **
54131 **     UNQLITE_ACCESS_EXISTS: Return 1 if the file exists
54132 **     UNQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
54133 **     UNQLITE_ACCESS_READONLY: Return 1 if the file is readable.
54134 **
54135 ** Otherwise return 0.
54136 */
unixAccess(unqlite_vfs * NotUsed,const char * zPath,int flags,int * pResOut)54137 static int unixAccess(
54138   unqlite_vfs *NotUsed,   /* The VFS containing this xAccess method */
54139   const char *zPath,      /* Path of the file to examine */
54140   int flags,              /* What do we want to learn about the zPath file? */
54141   int *pResOut            /* Write result boolean here */
54142 ){
54143   int amode = 0;
54144   SXUNUSED(NotUsed);
54145   switch( flags ){
54146     case UNQLITE_ACCESS_EXISTS:
54147       amode = F_OK;
54148       break;
54149     case UNQLITE_ACCESS_READWRITE:
54150       amode = W_OK|R_OK;
54151       break;
54152     case UNQLITE_ACCESS_READ:
54153       amode = R_OK;
54154       break;
54155     default:
54156 		/* Can't happen */
54157       break;
54158   }
54159   *pResOut = (access(zPath, amode)==0);
54160   if( flags==UNQLITE_ACCESS_EXISTS && *pResOut ){
54161     struct stat buf;
54162     if( 0==stat(zPath, &buf) && buf.st_size==0 ){
54163       *pResOut = 0;
54164     }
54165   }
54166   return UNQLITE_OK;
54167 }
54168 /*
54169 ** Turn a relative pathname into a full pathname. The relative path
54170 ** is stored as a nul-terminated string in the buffer pointed to by
54171 ** zPath.
54172 **
54173 ** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes
54174 ** (in this case, MAX_PATHNAME bytes). The full-path is written to
54175 ** this buffer before returning.
54176 */
unixFullPathname(unqlite_vfs * pVfs,const char * zPath,int nOut,char * zOut)54177 static int unixFullPathname(
54178   unqlite_vfs *pVfs,            /* Pointer to vfs object */
54179   const char *zPath,            /* Possibly relative input path */
54180   int nOut,                     /* Size of output buffer in bytes */
54181   char *zOut                    /* Output buffer */
54182 ){
54183   if( zPath[0]=='/' ){
54184 	  Systrcpy(zOut,(sxu32)nOut,zPath,0);
54185 	  SXUNUSED(pVfs);
54186   }else{
54187     sxu32 nCwd;
54188 	zOut[nOut-1] = '\0';
54189     if( getcwd(zOut, nOut-1)==0 ){
54190 		return UNQLITE_IOERR;
54191     }
54192     nCwd = SyStrlen(zOut);
54193     SyBufferFormat(&zOut[nCwd],(sxu32)nOut-nCwd,"/%s",zPath);
54194   }
54195   return UNQLITE_OK;
54196 }
54197 /*
54198  * Export the Unix Vfs.
54199  */
unqliteExportBuiltinVfs(void)54200 UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
54201 {
54202 	static const unqlite_vfs sUnixvfs = {
54203 		"Unix",              /* Vfs name */
54204 		1,                   /* Vfs structure version */
54205 		sizeof(unixFile),    /* szOsFile */
54206 		MAX_PATHNAME,        /* mxPathName */
54207 		unixOpen,            /* xOpen */
54208 		unixDelete,          /* xDelete */
54209 		unixAccess,          /* xAccess */
54210 		unixFullPathname,    /* xFullPathname */
54211 		0,                   /* xTmp */
54212 		unixSleep,           /* xSleep */
54213 		unixCurrentTime,     /* xCurrentTime */
54214 		0,                   /* xGetLastError */
54215 	};
54216 	return &sUnixvfs;
54217 }
54218 
54219 #endif /* __UNIXES__ */
54220 
54221 /* os_win.c */
54222 /*
54223  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
54224  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
54225  * Version 1.1.6
54226  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
54227  * please contact Symisc Systems via:
54228  *       legal@symisc.net
54229  *       licensing@symisc.net
54230  *       contact@symisc.net
54231  * or visit:
54232  *      http://unqlite.org/licensing.html
54233  */
54234  /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel <chm@symisc.net> $ */
54235 #ifndef UNQLITE_AMALGAMATION
54236 #include "unqliteInt.h"
54237 #endif
54238 /* Omit the whole layer from the build if compiling for platforms other than Windows */
54239 #ifdef __WINNT__
54240 /* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */
54241 #include <Windows.h>
54242 /*
54243 ** Some microsoft compilers lack this definition.
54244 */
54245 #ifndef INVALID_FILE_ATTRIBUTES
54246 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
54247 #endif
54248 /*
54249 ** WinCE lacks native support for file locking so we have to fake it
54250 ** with some code of our own.
54251 */
54252 #ifdef __WIN_CE__
54253 typedef struct winceLock {
54254   int nReaders;       /* Number of reader locks obtained */
54255   BOOL bPending;      /* Indicates a pending lock has been obtained */
54256   BOOL bReserved;     /* Indicates a reserved lock has been obtained */
54257   BOOL bExclusive;    /* Indicates an exclusive lock has been obtained */
54258 } winceLock;
54259 #define AreFileApisANSI() 1
54260 #define FormatMessageW(a,b,c,d,e,f,g) 0
54261 #endif
54262 
54263 /*
54264 ** The winFile structure is a subclass of unqlite_file* specific to the win32
54265 ** portability layer.
54266 */
54267 typedef struct winFile winFile;
54268 struct winFile {
54269   const unqlite_io_methods *pMethod; /*** Must be first ***/
54270   unqlite_vfs *pVfs;      /* The VFS used to open this file */
54271   HANDLE h;               /* Handle for accessing the file */
54272   sxu8 locktype;          /* Type of lock currently held on this file */
54273   short sharedLockByte;   /* Randomly chosen byte used as a shared lock */
54274   DWORD lastErrno;        /* The Windows errno from the last I/O error */
54275   DWORD sectorSize;       /* Sector size of the device file is on */
54276   int szChunk;            /* Chunk size */
54277 #ifdef __WIN_CE__
54278   WCHAR *zDeleteOnClose;  /* Name of file to delete when closing */
54279   HANDLE hMutex;          /* Mutex used to control access to shared lock */
54280   HANDLE hShared;         /* Shared memory segment used for locking */
54281   winceLock local;        /* Locks obtained by this instance of winFile */
54282   winceLock *shared;      /* Global shared lock memory for the file  */
54283 #endif
54284 };
54285 /*
54286 ** Convert a UTF-8 string to microsoft unicode (UTF-16?).
54287 **
54288 ** Space to hold the returned string is obtained from HeapAlloc().
54289 */
utf8ToUnicode(const char * zFilename)54290 static WCHAR *utf8ToUnicode(const char *zFilename){
54291   int nChar;
54292   WCHAR *zWideFilename;
54293 
54294   nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
54295   zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) );
54296   if( zWideFilename==0 ){
54297     return 0;
54298   }
54299   nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
54300   if( nChar==0 ){
54301     HeapFree(GetProcessHeap(),0,zWideFilename);
54302     zWideFilename = 0;
54303   }
54304   return zWideFilename;
54305 }
54306 
54307 /*
54308 ** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
54309 ** obtained from malloc().
54310 */
unicodeToUtf8(const WCHAR * zWideFilename)54311 static char *unicodeToUtf8(const WCHAR *zWideFilename){
54312   int nByte;
54313   char *zFilename;
54314 
54315   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
54316   zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte );
54317   if( zFilename==0 ){
54318     return 0;
54319   }
54320   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
54321                               0, 0);
54322   if( nByte == 0 ){
54323     HeapFree(GetProcessHeap(),0,zFilename);
54324     zFilename = 0;
54325   }
54326   return zFilename;
54327 }
54328 
54329 /*
54330 ** Convert an ansi string to microsoft unicode, based on the
54331 ** current codepage settings for file apis.
54332 **
54333 ** Space to hold the returned string is obtained
54334 ** from malloc.
54335 */
mbcsToUnicode(const char * zFilename)54336 static WCHAR *mbcsToUnicode(const char *zFilename){
54337   int nByte;
54338   WCHAR *zMbcsFilename;
54339   int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
54340 
54341   nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR);
54342   zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) );
54343   if( zMbcsFilename==0 ){
54344     return 0;
54345   }
54346   nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
54347   if( nByte==0 ){
54348     HeapFree(GetProcessHeap(),0,zMbcsFilename);
54349     zMbcsFilename = 0;
54350   }
54351   return zMbcsFilename;
54352 }
54353 /*
54354 ** Convert multibyte character string to UTF-8.  Space to hold the
54355 ** returned string is obtained from malloc().
54356 */
unqlite_win32_mbcs_to_utf8(const char * zFilename)54357 char *unqlite_win32_mbcs_to_utf8(const char *zFilename){
54358   char *zFilenameUtf8;
54359   WCHAR *zTmpWide;
54360 
54361   zTmpWide = mbcsToUnicode(zFilename);
54362   if( zTmpWide==0 ){
54363     return 0;
54364   }
54365   zFilenameUtf8 = unicodeToUtf8(zTmpWide);
54366   HeapFree(GetProcessHeap(),0,zTmpWide);
54367   return zFilenameUtf8;
54368 }
54369 /*
54370 ** Some microsoft compilers lack this definition.
54371 */
54372 #ifndef INVALID_SET_FILE_POINTER
54373 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
54374 #endif
54375 
54376 /*
54377 ** Move the current position of the file handle passed as the first
54378 ** argument to offset iOffset within the file. If successful, return 0.
54379 ** Otherwise, set pFile->lastErrno and return non-zero.
54380 */
seekWinFile(winFile * pFile,unqlite_int64 iOffset)54381 static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){
54382   LONG upperBits;                 /* Most sig. 32 bits of new offset */
54383   LONG lowerBits;                 /* Least sig. 32 bits of new offset */
54384   DWORD dwRet;                    /* Value returned by SetFilePointer() */
54385 
54386   upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
54387   lowerBits = (LONG)(iOffset & 0xffffffff);
54388 
54389   /* API oddity: If successful, SetFilePointer() returns a dword
54390   ** containing the lower 32-bits of the new file-offset. Or, if it fails,
54391   ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
54392   ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
54393   ** whether an error has actually occured, it is also necessary to call
54394   ** GetLastError().
54395   */
54396   dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
54397   if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
54398     pFile->lastErrno = GetLastError();
54399     return 1;
54400   }
54401   return 0;
54402 }
54403 /*
54404 ** Close a file.
54405 **
54406 ** It is reported that an attempt to close a handle might sometimes
54407 ** fail.  This is a very unreasonable result, but windows is notorious
54408 ** for being unreasonable so I do not doubt that it might happen.  If
54409 ** the close fails, we pause for 100 milliseconds and try again.  As
54410 ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
54411 ** giving up and returning an error.
54412 */
54413 #define MX_CLOSE_ATTEMPT 3
winClose(unqlite_file * id)54414 static int winClose(unqlite_file *id)
54415 {
54416   int rc, cnt = 0;
54417   winFile *pFile = (winFile*)id;
54418   do{
54419     rc = CloseHandle(pFile->h);
54420   }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
54421 
54422   return rc ? UNQLITE_OK : UNQLITE_IOERR;
54423 }
54424 /*
54425 ** Read data from a file into a buffer.  Return UNQLITE_OK if all
54426 ** bytes were read successfully and UNQLITE_IOERR if anything goes
54427 ** wrong.
54428 */
winRead(unqlite_file * id,void * pBuf,unqlite_int64 amt,unqlite_int64 offset)54429 static int winRead(
54430   unqlite_file *id,          /* File to read from */
54431   void *pBuf,                /* Write content into this buffer */
54432   unqlite_int64 amt,        /* Number of bytes to read */
54433   unqlite_int64 offset       /* Begin reading at this offset */
54434 ){
54435   winFile *pFile = (winFile*)id;  /* file handle */
54436   DWORD nRead;                    /* Number of bytes actually read from file */
54437 
54438   if( seekWinFile(pFile, offset) ){
54439     return UNQLITE_FULL;
54440   }
54441   if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){
54442     pFile->lastErrno = GetLastError();
54443     return UNQLITE_IOERR;
54444   }
54445   if( nRead<(DWORD)amt ){
54446     /* Unread parts of the buffer must be zero-filled */
54447     SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead));
54448     return UNQLITE_IOERR;
54449   }
54450 
54451   return UNQLITE_OK;
54452 }
54453 
54454 /*
54455 ** Write data from a buffer into a file.  Return UNQLITE_OK on success
54456 ** or some other error code on failure.
54457 */
winWrite(unqlite_file * id,const void * pBuf,unqlite_int64 amt,unqlite_int64 offset)54458 static int winWrite(
54459   unqlite_file *id,               /* File to write into */
54460   const void *pBuf,               /* The bytes to be written */
54461   unqlite_int64 amt,                        /* Number of bytes to write */
54462   unqlite_int64 offset            /* Offset into the file to begin writing at */
54463 ){
54464   int rc;                         /* True if error has occured, else false */
54465   winFile *pFile = (winFile*)id;  /* File handle */
54466 
54467   rc = seekWinFile(pFile, offset);
54468   if( rc==0 ){
54469     sxu8 *aRem = (sxu8 *)pBuf;        /* Data yet to be written */
54470     unqlite_int64 nRem = amt;         /* Number of bytes yet to be written */
54471     DWORD nWrite;                 /* Bytes written by each WriteFile() call */
54472 
54473     while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){
54474       aRem += nWrite;
54475       nRem -= nWrite;
54476     }
54477     if( nRem>0 ){
54478       pFile->lastErrno = GetLastError();
54479       rc = 1;
54480     }
54481   }
54482   if( rc ){
54483     if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
54484       return UNQLITE_FULL;
54485     }
54486     return UNQLITE_IOERR;
54487   }
54488   return UNQLITE_OK;
54489 }
54490 
54491 /*
54492 ** Truncate an open file to a specified size
54493 */
winTruncate(unqlite_file * id,unqlite_int64 nByte)54494 static int winTruncate(unqlite_file *id, unqlite_int64 nByte){
54495   winFile *pFile = (winFile*)id;  /* File handle object */
54496   int rc = UNQLITE_OK;             /* Return code for this function */
54497 
54498 
54499   /* If the user has configured a chunk-size for this file, truncate the
54500   ** file so that it consists of an integer number of chunks (i.e. the
54501   ** actual file size after the operation may be larger than the requested
54502   ** size).
54503   */
54504   if( pFile->szChunk ){
54505     nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
54506   }
54507 
54508   /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
54509   if( seekWinFile(pFile, nByte) ){
54510     rc = UNQLITE_IOERR;
54511   }else if( 0==SetEndOfFile(pFile->h) ){
54512     pFile->lastErrno = GetLastError();
54513     rc = UNQLITE_IOERR;
54514   }
54515   return rc;
54516 }
54517 /*
54518 ** Make sure all writes to a particular file are committed to disk.
54519 */
winSync(unqlite_file * id,int flags)54520 static int winSync(unqlite_file *id, int flags){
54521   winFile *pFile = (winFile*)id;
54522   SXUNUSED(flags); /* MSVC warning */
54523   if( FlushFileBuffers(pFile->h) ){
54524     return UNQLITE_OK;
54525   }else{
54526     pFile->lastErrno = GetLastError();
54527     return UNQLITE_IOERR;
54528   }
54529 }
54530 /*
54531 ** Determine the current size of a file in bytes
54532 */
winFileSize(unqlite_file * id,unqlite_int64 * pSize)54533 static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){
54534   DWORD upperBits;
54535   DWORD lowerBits;
54536   winFile *pFile = (winFile*)id;
54537   DWORD error;
54538   lowerBits = GetFileSize(pFile->h, &upperBits);
54539   if(   (lowerBits == INVALID_FILE_SIZE)
54540      && ((error = GetLastError()) != NO_ERROR) )
54541   {
54542     pFile->lastErrno = error;
54543     return UNQLITE_IOERR;
54544   }
54545   *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits;
54546   return UNQLITE_OK;
54547 }
54548 /*
54549 ** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
54550 */
54551 #ifndef LOCKFILE_FAIL_IMMEDIATELY
54552 # define LOCKFILE_FAIL_IMMEDIATELY 1
54553 #endif
54554 
54555 /*
54556 ** Acquire a reader lock.
54557 */
getReadLock(winFile * pFile)54558 static int getReadLock(winFile *pFile){
54559   int res;
54560   OVERLAPPED ovlp;
54561   ovlp.Offset = SHARED_FIRST;
54562   ovlp.OffsetHigh = 0;
54563   ovlp.hEvent = 0;
54564   res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp);
54565   if( res == 0 ){
54566     pFile->lastErrno = GetLastError();
54567   }
54568   return res;
54569 }
54570 /*
54571 ** Undo a readlock
54572 */
unlockReadLock(winFile * pFile)54573 static int unlockReadLock(winFile *pFile){
54574   int res;
54575   res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54576   if( res == 0 ){
54577     pFile->lastErrno = GetLastError();
54578   }
54579   return res;
54580 }
54581 /*
54582 ** Lock the file with the lock specified by parameter locktype - one
54583 ** of the following:
54584 **
54585 **     (1) SHARED_LOCK
54586 **     (2) RESERVED_LOCK
54587 **     (3) PENDING_LOCK
54588 **     (4) EXCLUSIVE_LOCK
54589 **
54590 ** Sometimes when requesting one lock state, additional lock states
54591 ** are inserted in between.  The locking might fail on one of the later
54592 ** transitions leaving the lock state different from what it started but
54593 ** still short of its goal.  The following chart shows the allowed
54594 ** transitions and the inserted intermediate states:
54595 **
54596 **    UNLOCKED -> SHARED
54597 **    SHARED -> RESERVED
54598 **    SHARED -> (PENDING) -> EXCLUSIVE
54599 **    RESERVED -> (PENDING) -> EXCLUSIVE
54600 **    PENDING -> EXCLUSIVE
54601 **
54602 ** This routine will only increase a lock.  The winUnlock() routine
54603 ** erases all locks at once and returns us immediately to locking level 0.
54604 ** It is not possible to lower the locking level one step at a time.  You
54605 ** must go straight to locking level 0.
54606 */
winLock(unqlite_file * id,int locktype)54607 static int winLock(unqlite_file *id, int locktype){
54608   int rc = UNQLITE_OK;    /* Return code from subroutines */
54609   int res = 1;           /* Result of a windows lock call */
54610   int newLocktype;       /* Set pFile->locktype to this value before exiting */
54611   int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
54612   winFile *pFile = (winFile*)id;
54613   DWORD error = NO_ERROR;
54614 
54615   /* If there is already a lock of this type or more restrictive on the
54616   ** OsFile, do nothing.
54617   */
54618   if( pFile->locktype>=locktype ){
54619     return UNQLITE_OK;
54620   }
54621 
54622   /* Make sure the locking sequence is correct
54623   assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
54624   assert( locktype!=PENDING_LOCK );
54625   assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
54626   */
54627   /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
54628   ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
54629   ** the PENDING_LOCK byte is temporary.
54630   */
54631   newLocktype = pFile->locktype;
54632   if(   (pFile->locktype==NO_LOCK)
54633      || (   (locktype==EXCLUSIVE_LOCK)
54634          && (pFile->locktype==RESERVED_LOCK))
54635   ){
54636     int cnt = 3;
54637     while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
54638       /* Try 3 times to get the pending lock.  The pending lock might be
54639       ** held by another reader process who will release it momentarily.
54640 	  */
54641       Sleep(1);
54642     }
54643     gotPendingLock = res;
54644     if( !res ){
54645       error = GetLastError();
54646     }
54647   }
54648 
54649   /* Acquire a shared lock
54650   */
54651   if( locktype==SHARED_LOCK && res ){
54652    /* assert( pFile->locktype==NO_LOCK ); */
54653     res = getReadLock(pFile);
54654     if( res ){
54655       newLocktype = SHARED_LOCK;
54656     }else{
54657       error = GetLastError();
54658     }
54659   }
54660 
54661   /* Acquire a RESERVED lock
54662   */
54663   if( locktype==RESERVED_LOCK && res ){
54664     /* assert( pFile->locktype==SHARED_LOCK ); */
54665     res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54666     if( res ){
54667       newLocktype = RESERVED_LOCK;
54668     }else{
54669       error = GetLastError();
54670     }
54671   }
54672 
54673   /* Acquire a PENDING lock
54674   */
54675   if( locktype==EXCLUSIVE_LOCK && res ){
54676     newLocktype = PENDING_LOCK;
54677     gotPendingLock = 0;
54678   }
54679 
54680   /* Acquire an EXCLUSIVE lock
54681   */
54682   if( locktype==EXCLUSIVE_LOCK && res ){
54683     /* assert( pFile->locktype>=SHARED_LOCK ); */
54684     res = unlockReadLock(pFile);
54685     res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54686     if( res ){
54687       newLocktype = EXCLUSIVE_LOCK;
54688     }else{
54689       error = GetLastError();
54690       getReadLock(pFile);
54691     }
54692   }
54693 
54694   /* If we are holding a PENDING lock that ought to be released, then
54695   ** release it now.
54696   */
54697   if( gotPendingLock && locktype==SHARED_LOCK ){
54698     UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
54699   }
54700 
54701   /* Update the state of the lock has held in the file descriptor then
54702   ** return the appropriate result code.
54703   */
54704   if( res ){
54705     rc = UNQLITE_OK;
54706   }else{
54707     pFile->lastErrno = error;
54708     rc = UNQLITE_BUSY;
54709   }
54710   pFile->locktype = (sxu8)newLocktype;
54711   return rc;
54712 }
54713 /*
54714 ** This routine checks if there is a RESERVED lock held on the specified
54715 ** file by this or any other process. If such a lock is held, return
54716 ** non-zero, otherwise zero.
54717 */
winCheckReservedLock(unqlite_file * id,int * pResOut)54718 static int winCheckReservedLock(unqlite_file *id, int *pResOut){
54719   int rc;
54720   winFile *pFile = (winFile*)id;
54721   if( pFile->locktype>=RESERVED_LOCK ){
54722     rc = 1;
54723   }else{
54724     rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54725     if( rc ){
54726       UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54727     }
54728     rc = !rc;
54729   }
54730   *pResOut = rc;
54731   return UNQLITE_OK;
54732 }
54733 /*
54734 ** Lower the locking level on file descriptor id to locktype.  locktype
54735 ** must be either NO_LOCK or SHARED_LOCK.
54736 **
54737 ** If the locking level of the file descriptor is already at or below
54738 ** the requested locking level, this routine is a no-op.
54739 **
54740 ** It is not possible for this routine to fail if the second argument
54741 ** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine
54742 ** might return UNQLITE_IOERR;
54743 */
winUnlock(unqlite_file * id,int locktype)54744 static int winUnlock(unqlite_file *id, int locktype){
54745   int type;
54746   winFile *pFile = (winFile*)id;
54747   int rc = UNQLITE_OK;
54748 
54749   type = pFile->locktype;
54750   if( type>=EXCLUSIVE_LOCK ){
54751     UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
54752     if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
54753       /* This should never happen.  We should always be able to
54754       ** reacquire the read lock */
54755       rc = UNQLITE_IOERR;
54756     }
54757   }
54758   if( type>=RESERVED_LOCK ){
54759     UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
54760   }
54761   if( locktype==NO_LOCK && type>=SHARED_LOCK ){
54762     unlockReadLock(pFile);
54763   }
54764   if( type>=PENDING_LOCK ){
54765     UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
54766   }
54767   pFile->locktype = (sxu8)locktype;
54768   return rc;
54769 }
54770 /*
54771 ** Return the sector size in bytes of the underlying block device for
54772 ** the specified file. This is almost always 512 bytes, but may be
54773 ** larger for some devices.
54774 **
54775 */
winSectorSize(unqlite_file * id)54776 static int winSectorSize(unqlite_file *id){
54777   return (int)(((winFile*)id)->sectorSize);
54778 }
54779 /*
54780 ** This vector defines all the methods that can operate on an
54781 ** unqlite_file for Windows systems.
54782 */
54783 static const unqlite_io_methods winIoMethod = {
54784   1,                              /* iVersion */
54785   winClose,                       /* xClose */
54786   winRead,                        /* xRead */
54787   winWrite,                       /* xWrite */
54788   winTruncate,                    /* xTruncate */
54789   winSync,                        /* xSync */
54790   winFileSize,                    /* xFileSize */
54791   winLock,                        /* xLock */
54792   winUnlock,                      /* xUnlock */
54793   winCheckReservedLock,           /* xCheckReservedLock */
54794   winSectorSize,                  /* xSectorSize */
54795 };
54796 /*
54797  * Windows VFS Methods.
54798  */
54799 /*
54800 ** Convert a UTF-8 filename into whatever form the underlying
54801 ** operating system wants filenames in.  Space to hold the result
54802 ** is obtained from malloc and must be freed by the calling
54803 ** function.
54804 */
convertUtf8Filename(const char * zFilename)54805 static void *convertUtf8Filename(const char *zFilename)
54806 {
54807   void *zConverted;
54808   zConverted = utf8ToUnicode(zFilename);
54809   /* caller will handle out of memory */
54810   return zConverted;
54811 }
54812 /*
54813 ** Delete the named file.
54814 **
54815 ** Note that windows does not allow a file to be deleted if some other
54816 ** process has it open.  Sometimes a virus scanner or indexing program
54817 ** will open a journal file shortly after it is created in order to do
54818 ** whatever it does.  While this other process is holding the
54819 ** file open, we will be unable to delete it.  To work around this
54820 ** problem, we delay 100 milliseconds and try to delete again.  Up
54821 ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
54822 ** up and returning an error.
54823 */
54824 #define MX_DELETION_ATTEMPTS 5
winDelete(unqlite_vfs * pVfs,const char * zFilename,int syncDir)54825 static int winDelete(
54826   unqlite_vfs *pVfs,          /* Not used on win32 */
54827   const char *zFilename,      /* Name of file to delete */
54828   int syncDir                 /* Not used on win32 */
54829 ){
54830   int cnt = 0;
54831   DWORD rc;
54832   DWORD error = 0;
54833   void *zConverted;
54834   zConverted = convertUtf8Filename(zFilename);
54835   if( zConverted==0 ){
54836 	   SXUNUSED(pVfs);
54837 	   SXUNUSED(syncDir);
54838     return UNQLITE_NOMEM;
54839   }
54840   do{
54841 	  DeleteFileW((LPCWSTR)zConverted);
54842   }while(   (   ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES)
54843 	  || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
54844 	  && (++cnt < MX_DELETION_ATTEMPTS)
54845 	  && (Sleep(100), 1)
54846 	  );
54847 	HeapFree(GetProcessHeap(),0,zConverted);
54848 
54849   return (   (rc == INVALID_FILE_ATTRIBUTES)
54850           && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR;
54851 }
54852 /*
54853 ** Check the existance and status of a file.
54854 */
winAccess(unqlite_vfs * pVfs,const char * zFilename,int flags,int * pResOut)54855 static int winAccess(
54856   unqlite_vfs *pVfs,         /* Not used  */
54857   const char *zFilename,     /* Name of file to check */
54858   int flags,                 /* Type of test to make on this file */
54859   int *pResOut               /* OUT: Result */
54860 ){
54861   WIN32_FILE_ATTRIBUTE_DATA sAttrData;
54862   DWORD attr;
54863   int rc = 0;
54864   void *zConverted;
54865   SXUNUSED(pVfs);
54866 
54867   zConverted = convertUtf8Filename(zFilename);
54868   if( zConverted==0 ){
54869     return UNQLITE_NOMEM;
54870   }
54871   SyZero(&sAttrData,sizeof(sAttrData));
54872   if( GetFileAttributesExW((WCHAR*)zConverted,
54873 	  GetFileExInfoStandard,
54874 	  &sAttrData) ){
54875       /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file
54876       ** as if it does not exist.
54877       */
54878       if(    flags==UNQLITE_ACCESS_EXISTS
54879           && sAttrData.nFileSizeHigh==0
54880           && sAttrData.nFileSizeLow==0 ){
54881         attr = INVALID_FILE_ATTRIBUTES;
54882       }else{
54883         attr = sAttrData.dwFileAttributes;
54884       }
54885     }else{
54886       if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
54887         HeapFree(GetProcessHeap(),0,zConverted);
54888         return UNQLITE_IOERR;
54889       }else{
54890         attr = INVALID_FILE_ATTRIBUTES;
54891       }
54892     }
54893   HeapFree(GetProcessHeap(),0,zConverted);
54894   switch( flags ){
54895      case UNQLITE_ACCESS_READWRITE:
54896       rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
54897       break;
54898     case UNQLITE_ACCESS_READ:
54899     case UNQLITE_ACCESS_EXISTS:
54900 	default:
54901       rc = attr!=INVALID_FILE_ATTRIBUTES;
54902       break;
54903   }
54904   *pResOut = rc;
54905   return UNQLITE_OK;
54906 }
54907 /*
54908 ** Turn a relative pathname into a full pathname.  Write the full
54909 ** pathname into zOut[].  zOut[] will be at least pVfs->mxPathname
54910 ** bytes in size.
54911 */
winFullPathname(unqlite_vfs * pVfs,const char * zRelative,int nFull,char * zFull)54912 static int winFullPathname(
54913   unqlite_vfs *pVfs,            /* Pointer to vfs object */
54914   const char *zRelative,        /* Possibly relative input path */
54915   int nFull,                    /* Size of output buffer in bytes */
54916   char *zFull                   /* Output buffer */
54917 ){
54918   int nByte;
54919   void *zConverted;
54920   WCHAR *zTemp;
54921   char *zOut;
54922   SXUNUSED(nFull);
54923   zConverted = convertUtf8Filename(zRelative);
54924   if( zConverted == 0 ){
54925 	  return UNQLITE_NOMEM;
54926   }
54927   nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
54928   zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) );
54929   if( zTemp==0 ){
54930 	  HeapFree(GetProcessHeap(),0,zConverted);
54931 	  return UNQLITE_NOMEM;
54932   }
54933   GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
54934   HeapFree(GetProcessHeap(),0,zConverted);
54935   zOut = unicodeToUtf8(zTemp);
54936   HeapFree(GetProcessHeap(),0,zTemp);
54937   if( zOut == 0 ){
54938     return UNQLITE_NOMEM;
54939   }
54940   Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0);
54941   HeapFree(GetProcessHeap(),0,zOut);
54942   return UNQLITE_OK;
54943 }
54944 /*
54945 ** Get the sector size of the device used to store
54946 ** file.
54947 */
getSectorSize(unqlite_vfs * pVfs,const char * zRelative)54948 static int getSectorSize(
54949     unqlite_vfs *pVfs,
54950     const char *zRelative     /* UTF-8 file name */
54951 ){
54952   DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
54953   char zFullpath[MAX_PATH+1];
54954   int rc;
54955   DWORD dwRet = 0;
54956   DWORD dwDummy;
54957   /*
54958   ** We need to get the full path name of the file
54959   ** to get the drive letter to look up the sector
54960   ** size.
54961   */
54962   rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
54963   if( rc == UNQLITE_OK )
54964   {
54965     void *zConverted = convertUtf8Filename(zFullpath);
54966     if( zConverted ){
54967         /* trim path to just drive reference */
54968         WCHAR *p = (WCHAR *)zConverted;
54969         for(;*p;p++){
54970           if( *p == '\\' ){
54971             *p = '\0';
54972             break;
54973           }
54974         }
54975         dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
54976                                   &dwDummy,
54977                                   &bytesPerSector,
54978                                   &dwDummy,
54979                                   &dwDummy);
54980 		 HeapFree(GetProcessHeap(),0,zConverted);
54981 	}
54982     if( !dwRet ){
54983       bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
54984     }
54985   }
54986   return (int) bytesPerSector;
54987 }
54988 /*
54989 ** Sleep for a little while.  Return the amount of time slept.
54990 */
winSleep(unqlite_vfs * pVfs,int microsec)54991 static int winSleep(unqlite_vfs *pVfs, int microsec){
54992   Sleep((microsec+999)/1000);
54993   SXUNUSED(pVfs);
54994   return ((microsec+999)/1000)*1000;
54995 }
54996 /*
54997  * Export the current system time.
54998  */
winCurrentTime(unqlite_vfs * pVfs,Sytm * pOut)54999 static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
55000 {
55001 	SYSTEMTIME sSys;
55002 	SXUNUSED(pVfs);
55003 	GetSystemTime(&sSys);
55004 	SYSTEMTIME_TO_SYTM(&sSys,pOut);
55005 	return UNQLITE_OK;
55006 }
55007 /*
55008 ** The idea is that this function works like a combination of
55009 ** GetLastError() and FormatMessage() on windows (or errno and
55010 ** strerror_r() on unix). After an error is returned by an OS
55011 ** function, UnQLite calls this function with zBuf pointing to
55012 ** a buffer of nBuf bytes. The OS layer should populate the
55013 ** buffer with a nul-terminated UTF-8 encoded error message
55014 ** describing the last IO error to have occurred within the calling
55015 ** thread.
55016 **
55017 ** If the error message is too large for the supplied buffer,
55018 ** it should be truncated. The return value of xGetLastError
55019 ** is zero if the error message fits in the buffer, or non-zero
55020 ** otherwise (if the message was truncated). If non-zero is returned,
55021 ** then it is not necessary to include the nul-terminator character
55022 ** in the output buffer.
55023 */
winGetLastError(unqlite_vfs * pVfs,int nBuf,char * zBuf)55024 static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf)
55025 {
55026   /* FormatMessage returns 0 on failure.  Otherwise it
55027   ** returns the number of TCHARs written to the output
55028   ** buffer, excluding the terminating null char.
55029   */
55030   DWORD error = GetLastError();
55031   WCHAR *zTempWide = 0;
55032   DWORD dwLen;
55033   char *zOut = 0;
55034 
55035   SXUNUSED(pVfs);
55036   dwLen = FormatMessageW(
55037 	  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
55038 	  0,
55039 	  error,
55040 	  0,
55041 	  (LPWSTR) &zTempWide,
55042 	  0,
55043 	  0
55044 	  );
55045     if( dwLen > 0 ){
55046       /* allocate a buffer and convert to UTF8 */
55047       zOut = unicodeToUtf8(zTempWide);
55048       /* free the system buffer allocated by FormatMessage */
55049       LocalFree(zTempWide);
55050     }
55051 	if( 0 == dwLen ){
55052 		Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1);
55053 	}else{
55054 		/* copy a maximum of nBuf chars to output buffer */
55055 		Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */);
55056 		/* free the UTF8 buffer */
55057 		HeapFree(GetProcessHeap(),0,zOut);
55058 	}
55059   return 0;
55060 }
55061 /*
55062 ** Open a file.
55063 */
winOpen(unqlite_vfs * pVfs,const char * zName,unqlite_file * id,unsigned int flags)55064 static int winOpen(
55065   unqlite_vfs *pVfs,        /* Not used */
55066   const char *zName,        /* Name of the file (UTF-8) */
55067   unqlite_file *id,         /* Write the UnQLite file handle here */
55068   unsigned int flags                /* Open mode flags */
55069 ){
55070   HANDLE h;
55071   DWORD dwDesiredAccess;
55072   DWORD dwShareMode;
55073   DWORD dwCreationDisposition;
55074   DWORD dwFlagsAndAttributes = 0;
55075   winFile *pFile = (winFile*)id;
55076   void *zConverted;              /* Filename in OS encoding */
55077   const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
55078   int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
55079   int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
55080   int isCreate     = (flags & UNQLITE_OPEN_CREATE);
55081   int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
55082 
55083   pFile->h = INVALID_HANDLE_VALUE;
55084   /* Convert the filename to the system encoding. */
55085   zConverted = convertUtf8Filename(zUtf8Name);
55086   if( zConverted==0 ){
55087     return UNQLITE_NOMEM;
55088   }
55089   if( isReadWrite ){
55090     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
55091   }else{
55092     dwDesiredAccess = GENERIC_READ;
55093   }
55094   /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
55095   ** created.
55096   */
55097   if( isExclusive ){
55098     /* Creates a new file, only if it does not already exist. */
55099     /* If the file exists, it fails. */
55100     dwCreationDisposition = CREATE_NEW;
55101   }else if( isCreate ){
55102     /* Open existing file, or create if it doesn't exist */
55103     dwCreationDisposition = OPEN_ALWAYS;
55104   }else{
55105     /* Opens a file, only if it exists. */
55106     dwCreationDisposition = OPEN_EXISTING;
55107   }
55108 
55109   dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
55110 
55111   if( isDelete ){
55112     dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
55113                                | FILE_ATTRIBUTE_HIDDEN
55114                                | FILE_FLAG_DELETE_ON_CLOSE;
55115   }else{
55116     dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
55117   }
55118   h = CreateFileW((WCHAR*)zConverted,
55119        dwDesiredAccess,
55120        dwShareMode,
55121        NULL,
55122        dwCreationDisposition,
55123        dwFlagsAndAttributes,
55124        NULL
55125     );
55126   if( h==INVALID_HANDLE_VALUE ){
55127     pFile->lastErrno = GetLastError();
55128     HeapFree(GetProcessHeap(),0,zConverted);
55129 	return UNQLITE_IOERR;
55130   }
55131   SyZero(pFile,sizeof(*pFile));
55132   pFile->pMethod = &winIoMethod;
55133   pFile->h = h;
55134   pFile->lastErrno = NO_ERROR;
55135   pFile->pVfs = pVfs;
55136   pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
55137   HeapFree(GetProcessHeap(),0,zConverted);
55138   return UNQLITE_OK;
55139 }
55140 /*
55141  * Export the Windows Vfs.
55142  */
unqliteExportBuiltinVfs(void)55143 UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
55144 {
55145 	static const unqlite_vfs sWinvfs = {
55146 		"Windows",           /* Vfs name */
55147 		1,                   /* Vfs structure version */
55148 		sizeof(winFile),     /* szOsFile */
55149 		MAX_PATH,            /* mxPathName */
55150 		winOpen,             /* xOpen */
55151 		winDelete,           /* xDelete */
55152 		winAccess,           /* xAccess */
55153 		winFullPathname,     /* xFullPathname */
55154 		0,                   /* xTmp */
55155 		winSleep,            /* xSleep */
55156 		winCurrentTime,      /* xCurrentTime */
55157 		winGetLastError,     /* xGetLastError */
55158 	};
55159 	return &sWinvfs;
55160 }
55161 #endif /* __WINNT__ */
55162 
55163 /* pager.c */
55164 /*
55165  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
55166  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
55167  * Copyright (C) 2014, Yuras Shumovich <shumovichy@gmail.com>
55168  * Version 1.1.6
55169  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
55170  * please contact Symisc Systems via:
55171  *       legal@symisc.net
55172  *       licensing@symisc.net
55173  *       contact@symisc.net
55174  * or visit:
55175  *      http://unqlite.org/licensing.html
55176  */
55177  /* $SymiscID: pager.c v1.1 Win7 2012-11-29 03:46 stable <chm@symisc.net> $ */
55178 #ifndef UNQLITE_AMALGAMATION
55179 #include "unqliteInt.h"
55180 #endif
55181 /*
55182 ** This file implements the pager and the transaction manager for UnQLite (Mostly inspired from the SQLite3 Source tree).
55183 **
55184 ** The Pager.eState variable stores the current 'state' of a pager. A
55185 ** pager may be in any one of the seven states shown in the following
55186 ** state diagram.
55187 **
55188 **                            OPEN <------+------+
55189 **                              |         |      |
55190 **                              V         |      |
55191 **               +---------> READER-------+      |
55192 **               |              |                |
55193 **               |              V                |
55194 **               |<-------WRITER_LOCKED--------->|
55195 **               |              |                |
55196 **               |              V                |
55197 **               |<------WRITER_CACHEMOD-------->|
55198 **               |              |                |
55199 **               |              V                |
55200 **               |<-------WRITER_DBMOD---------->|
55201 **               |              |                |
55202 **               |              V                |
55203 **               +<------WRITER_FINISHED-------->+
55204 **
55205 **  OPEN:
55206 **
55207 **    The pager starts up in this state. Nothing is guaranteed in this
55208 **    state - the file may or may not be locked and the database size is
55209 **    unknown. The database may not be read or written.
55210 **
55211 **    * No read or write transaction is active.
55212 **    * Any lock, or no lock at all, may be held on the database file.
55213 **    * The dbSize and dbOrigSize variables may not be trusted.
55214 **
55215 **  READER:
55216 **
55217 **    In this state all the requirements for reading the database in
55218 **    rollback mode are met. Unless the pager is (or recently
55219 **    was) in exclusive-locking mode, a user-level read transaction is
55220 **    open. The database size is known in this state.
55221 **
55222 **    * A read transaction may be active (but a write-transaction cannot).
55223 **    * A SHARED or greater lock is held on the database file.
55224 **    * The dbSize variable may be trusted (even if a user-level read
55225 **      transaction is not active). The dbOrigSize variables
55226 **      may not be trusted at this point.
55227 **    * Even if a read-transaction is not open, it is guaranteed that
55228 **      there is no hot-journal in the file-system.
55229 **
55230 **  WRITER_LOCKED:
55231 **
55232 **    The pager moves to this state from READER when a write-transaction
55233 **    is first opened on the database. In WRITER_LOCKED state, all locks
55234 **    required to start a write-transaction are held, but no actual
55235 **    modifications to the cache or database have taken place.
55236 **
55237 **    In rollback mode, a RESERVED or (if the transaction was opened with
55238 **    EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when
55239 **    moving to this state, but the journal file is not written to or opened
55240 **    to in this state. If the transaction is committed or rolled back while
55241 **    in WRITER_LOCKED state, all that is required is to unlock the database
55242 **    file.
55243 **
55244 **    * A write transaction is active.
55245 **    * If the connection is open in rollback-mode, a RESERVED or greater
55246 **      lock is held on the database file.
55247 **    * The dbSize and dbOrigSize variables are all valid.
55248 **    * The contents of the pager cache have not been modified.
55249 **    * The journal file may or may not be open.
55250 **    * Nothing (not even the first header) has been written to the journal.
55251 **
55252 **  WRITER_CACHEMOD:
55253 **
55254 **    A pager moves from WRITER_LOCKED state to this state when a page is
55255 **    first modified by the upper layer. In rollback mode the journal file
55256 **    is opened (if it is not already open) and a header written to the
55257 **    start of it. The database file on disk has not been modified.
55258 **
55259 **    * A write transaction is active.
55260 **    * A RESERVED or greater lock is held on the database file.
55261 **    * The journal file is open and the first header has been written
55262 **      to it, but the header has not been synced to disk.
55263 **    * The contents of the page cache have been modified.
55264 **
55265 **  WRITER_DBMOD:
55266 **
55267 **    The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
55268 **    when it modifies the contents of the database file.
55269 **
55270 **    * A write transaction is active.
55271 **    * An EXCLUSIVE or greater lock is held on the database file.
55272 **    * The journal file is open and the first header has been written
55273 **      and synced to disk.
55274 **    * The contents of the page cache have been modified (and possibly
55275 **      written to disk).
55276 **
55277 **  WRITER_FINISHED:
55278 **
55279 **    A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
55280 **    state after the entire transaction has been successfully written into the
55281 **    database file. In this state the transaction may be committed simply
55282 **    by finalizing the journal file. Once in WRITER_FINISHED state, it is
55283 **    not possible to modify the database further. At this point, the upper
55284 **    layer must either commit or rollback the transaction.
55285 **
55286 **    * A write transaction is active.
55287 **    * An EXCLUSIVE or greater lock is held on the database file.
55288 **    * All writing and syncing of journal and database data has finished.
55289 **      If no error occured, all that remains is to finalize the journal to
55290 **      commit the transaction. If an error did occur, the caller will need
55291 **      to rollback the transaction.
55292 **
55293 **
55294 */
55295 #define PAGER_OPEN                  0
55296 #define PAGER_READER                1
55297 #define PAGER_WRITER_LOCKED         2
55298 #define PAGER_WRITER_CACHEMOD       3
55299 #define PAGER_WRITER_DBMOD          4
55300 #define PAGER_WRITER_FINISHED       5
55301 /*
55302 ** Journal files begin with the following magic string.  The data
55303 ** was obtained from /dev/random.  It is used only as a sanity check.
55304 **
55305 ** NOTE: These values must be different from the one used by SQLite3
55306 ** to avoid journal file collision.
55307 **
55308 */
55309 static const unsigned char aJournalMagic[] = {
55310   0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f,
55311 };
55312 /*
55313 ** The journal header size for this pager. This is usually the same
55314 ** size as a single disk sector. See also setSectorSize().
55315 */
55316 #define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize)
55317 /*
55318  * Database page handle.
55319  * Each raw disk page is represented in memory by an instance
55320  * of the following structure.
55321  */
55322 typedef struct Page Page;
55323 struct Page {
55324   /* Must correspond to unqlite_page */
55325   unsigned char *zData;           /* Content of this page */
55326   void *pUserData;                /* Extra content */
55327   pgno pgno;                      /* Page number for this page */
55328   /**********************************************************************
55329   ** Elements above are public.  All that follows is private to pcache.c
55330   ** and should not be accessed by other modules.
55331   */
55332   Pager *pPager;                 /* The pager this page is part of */
55333   int flags;                     /* Page flags defined below */
55334   int nRef;                      /* Number of users of this page */
55335   Page *pNext, *pPrev;    /* A list of all pages */
55336   Page *pDirtyNext;             /* Next element in list of dirty pages */
55337   Page *pDirtyPrev;             /* Previous element in list of dirty pages */
55338   Page *pNextCollide,*pPrevCollide; /* Collission chain */
55339   Page *pNextHot,*pPrevHot;    /* Hot dirty pages chain */
55340 };
55341 /* Bit values for Page.flags */
55342 #define PAGE_DIRTY             0x002  /* Page has changed */
55343 #define PAGE_NEED_SYNC         0x004  /* fsync the rollback journal before
55344                                        ** writing this page to the database */
55345 #define PAGE_DONT_WRITE        0x008  /* Dont write page content to disk */
55346 #define PAGE_NEED_READ         0x010  /* Content is unread */
55347 #define PAGE_IN_JOURNAL        0x020  /* Page written to the journal */
55348 #define PAGE_HOT_DIRTY         0x040  /* Hot dirty page */
55349 #define PAGE_DONT_MAKE_HOT     0x080  /* Dont make this page Hot. In other words,
55350 									   * do not link it to the hot dirty list.
55351 									   */
55352 /*
55353  * Each active database pager is represented by an instance of
55354  * the following structure.
55355  */
55356 struct Pager
55357 {
55358   SyMemBackend *pAllocator;      /* Memory backend */
55359   unqlite *pDb;                  /* DB handle that own this instance */
55360   unqlite_kv_engine *pEngine;    /* Underlying KV storage engine */
55361   char *zFilename;               /* Name of the database file */
55362   char *zJournal;                /* Name of the journal file */
55363   unqlite_vfs *pVfs;             /* Underlying virtual file system */
55364   unqlite_file *pfd,*pjfd;       /* File descriptors for database and journal */
55365   pgno dbSize;                   /* Number of pages in the file */
55366   pgno dbOrigSize;               /* dbSize before the current change */
55367   sxi64 dbByteSize;              /* Database size in bytes */
55368   void *pMmap;                   /* Read-only Memory view (mmap) of the whole file if requested (UNQLITE_OPEN_MMAP). */
55369   sxu32 nRec;                    /* Number of pages written to the journal */
55370   SyPRNGCtx sPrng;               /* PRNG Context */
55371   sxu32 cksumInit;               /* Quasi-random value added to every checksum */
55372   sxu32 iOpenFlags;              /* Flag passed to unqlite_open() after processing */
55373   sxi64 iJournalOfft;            /* Journal offset we are reading from */
55374   int (*xBusyHandler)(void *);   /* Busy handler */
55375   void *pBusyHandlerArg;         /* First arg to xBusyHandler() */
55376   void (*xPageUnpin)(void *);    /* Page Unpin callback */
55377   void (*xPageReload)(void *);   /* Page Reload callback */
55378   Bitvec *pVec;                  /* Bitmap */
55379   Page *pHeader;                 /* Page one of the database (Unqlite header) */
55380   Sytm tmCreate;                 /* Database creation time */
55381   SyString sKv;                  /* Underlying Key/Value storage engine name */
55382   int iState;                    /* Pager state */
55383   int iLock;                     /* Lock state */
55384   sxi32 iFlags;                  /* Control flags (see below) */
55385   int is_mem;                    /* True for an in-memory database */
55386   int is_rdonly;                 /* True for a read-only database */
55387   int no_jrnl;                   /* TRUE to omit journaling */
55388   int iPageSize;                 /* Page size in bytes (default 4K) */
55389   int iSectorSize;               /* Size of a single sector on disk */
55390   unsigned char *zTmpPage;       /* Temporary page */
55391   Page *pFirstDirty;             /* First dirty pages */
55392   Page *pDirty;                  /* Transient list of dirty pages */
55393   Page *pAll;                    /* List of all pages */
55394   Page *pHotDirty;               /* List of hot dirty pages */
55395   Page *pFirstHot;               /* First hot dirty page */
55396   sxu32 nHot;                    /* Total number of hot dirty pages */
55397   Page **apHash;                 /* Page table */
55398   sxu32 nSize;                   /* apHash[] size: Must be a power of two  */
55399   sxu32 nPage;                   /* Total number of page loaded in memory */
55400   sxu32 nCacheMax;               /* Maximum page to cache*/
55401 };
55402 /* Control flags */
55403 #define PAGER_CTRL_COMMIT_ERR   0x001 /* Commit error */
55404 #define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */
55405 /*
55406 ** Read a 32-bit integer from the given file descriptor.
55407 ** All values are stored on disk as big-endian.
55408 */
ReadInt32(unqlite_file * pFd,sxu32 * pOut,sxi64 iOfft)55409 static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft)
55410 {
55411 	unsigned char zBuf[4];
55412 	int rc;
55413 	rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
55414 	if( rc != UNQLITE_OK ){
55415 		return rc;
55416 	}
55417 	SyBigEndianUnpack32(zBuf,pOut);
55418 	return UNQLITE_OK;
55419 }
55420 /*
55421 ** Read a 64-bit integer from the given file descriptor.
55422 ** All values are stored on disk as big-endian.
55423 */
ReadInt64(unqlite_file * pFd,sxu64 * pOut,sxi64 iOfft)55424 static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft)
55425 {
55426 	unsigned char zBuf[8];
55427 	int rc;
55428 	rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
55429 	if( rc != UNQLITE_OK ){
55430 		return rc;
55431 	}
55432 	SyBigEndianUnpack64(zBuf,pOut);
55433 	return UNQLITE_OK;
55434 }
55435 /*
55436 ** Write a 32-bit integer into the given file descriptor.
55437 */
WriteInt32(unqlite_file * pFd,sxu32 iNum,sxi64 iOfft)55438 static int WriteInt32(unqlite_file *pFd,sxu32 iNum,sxi64 iOfft)
55439 {
55440 	unsigned char zBuf[4];
55441 	int rc;
55442 	SyBigEndianPack32(zBuf,iNum);
55443 	rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
55444 	return rc;
55445 }
55446 /*
55447 ** Write a 64-bit integer into the given file descriptor.
55448 */
WriteInt64(unqlite_file * pFd,sxu64 iNum,sxi64 iOfft)55449 static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft)
55450 {
55451 	unsigned char zBuf[8];
55452 	int rc;
55453 	SyBigEndianPack64(zBuf,iNum);
55454 	rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
55455 	return rc;
55456 }
55457 /*
55458 ** The maximum allowed sector size. 64KiB. If the xSectorsize() method
55459 ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
55460 ** This could conceivably cause corruption following a power failure on
55461 ** such a system. This is currently an undocumented limit.
55462 */
55463 #define MAX_SECTOR_SIZE 0x10000
55464 /*
55465 ** Get the size of a single sector on disk.
55466 ** The sector size will be used used  to determine the size
55467 ** and alignment of journal header and within created journal files.
55468 **
55469 ** The default sector size is set to 512.
55470 */
GetSectorSize(unqlite_file * pFd)55471 static int GetSectorSize(unqlite_file *pFd)
55472 {
55473 	int iSectorSize = UNQLITE_DEFAULT_SECTOR_SIZE;
55474 	if( pFd ){
55475 		iSectorSize = unqliteOsSectorSize(pFd);
55476 		if( iSectorSize < 32 ){
55477 			iSectorSize = 512;
55478 		}
55479 		if( iSectorSize > MAX_SECTOR_SIZE ){
55480 			iSectorSize = MAX_SECTOR_SIZE;
55481 		}
55482 	}
55483 	return iSectorSize;
55484 }
55485 /* Hash function for page number  */
55486 #define PAGE_HASH(PNUM) (PNUM)
55487 /*
55488  * Fetch a page from the cache.
55489  */
pager_fetch_page(Pager * pPager,pgno page_num)55490 static Page * pager_fetch_page(Pager *pPager,pgno page_num)
55491 {
55492 	Page *pEntry;
55493 	if( pPager->nPage < 1 ){
55494 		/* Don't bother hashing */
55495 		return 0;
55496 	}
55497 	/* Perform the lookup */
55498 	pEntry = pPager->apHash[PAGE_HASH(page_num) & (pPager->nSize - 1)];
55499 	for(;;){
55500 		if( pEntry == 0 ){
55501 			break;
55502 		}
55503 		if( pEntry->pgno == page_num ){
55504 			return pEntry;
55505 		}
55506 		/* Point to the next entry in the colission chain */
55507 		pEntry = pEntry->pNextCollide;
55508 	}
55509 	/* No such page */
55510 	return 0;
55511 }
55512 /*
55513  * Allocate and initialize a new page.
55514  */
pager_alloc_page(Pager * pPager,pgno num_page)55515 static Page * pager_alloc_page(Pager *pPager,pgno num_page)
55516 {
55517 	Page *pNew;
55518 
55519 	pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize);
55520 	if( pNew == 0 ){
55521 		return 0;
55522 	}
55523 	/* Zero the structure */
55524 	SyZero(pNew,sizeof(Page)+pPager->iPageSize);
55525 	/* Page data */
55526 	pNew->zData = (unsigned char *)&pNew[1];
55527 	/* Fill in the structure */
55528 	pNew->pPager = pPager;
55529 	pNew->nRef = 1;
55530 	pNew->pgno = num_page;
55531 	return pNew;
55532 }
55533 /*
55534  * Increment the reference count of a given page.
55535  */
page_ref(Page * pPage)55536 static void page_ref(Page *pPage)
55537 {
55538 	pPage->nRef++;
55539 }
55540 /*
55541  * Release an in-memory page after its reference count reach zero.
55542  */
pager_release_page(Pager * pPager,Page * pPage)55543 static int pager_release_page(Pager *pPager,Page *pPage)
55544 {
55545 	int rc = UNQLITE_OK;
55546 	if( !(pPage->flags & PAGE_DIRTY)){
55547 		/* Invoke the unpin callback if available */
55548 		if( pPager->xPageUnpin && pPage->pUserData ){
55549 			pPager->xPageUnpin(pPage->pUserData);
55550 		}
55551 		pPage->pUserData = 0;
55552 		SyMemBackendPoolFree(pPager->pAllocator,pPage);
55553 	}else{
55554 		/* Dirty page, it will be released later when a dirty commit
55555 		 * or the final commit have been applied.
55556 		 */
55557 		rc = UNQLITE_LOCKED;
55558 	}
55559 	return rc;
55560 }
55561 /* Forward declaration */
55562 static int pager_unlink_page(Pager *pPager,Page *pPage);
55563 /*
55564  * Decrement the reference count of a given page.
55565  */
page_unref(Page * pPage)55566 static void page_unref(Page *pPage)
55567 {
55568 	pPage->nRef--;
55569 	if( pPage->nRef < 1	){
55570 		Pager *pPager = pPage->pPager;
55571 		if( !(pPage->flags & PAGE_DIRTY)  ){
55572 			pager_unlink_page(pPager,pPage);
55573 			/* Release the page */
55574 			pager_release_page(pPager,pPage);
55575 		}else{
55576 			if( pPage->flags & PAGE_DONT_MAKE_HOT ){
55577 				/* Do not add this page to the hot dirty list */
55578 				return;
55579 			}
55580 			if( !(pPage->flags & PAGE_HOT_DIRTY) ){
55581 				/* Add to the hot dirty list */
55582 				pPage->pPrevHot = 0;
55583 				if( pPager->pFirstHot == 0 ){
55584 					pPager->pFirstHot = pPager->pHotDirty = pPage;
55585 				}else{
55586 					pPage->pNextHot = pPager->pHotDirty;
55587 					if( pPager->pHotDirty ){
55588 						pPager->pHotDirty->pPrevHot = pPage;
55589 					}
55590 					pPager->pHotDirty = pPage;
55591 				}
55592 				pPager->nHot++;
55593 				pPage->flags |= PAGE_HOT_DIRTY;
55594 			}
55595 		}
55596 	}
55597 }
55598 /*
55599  * Link a freshly created page to the list of active page.
55600  */
pager_link_page(Pager * pPager,Page * pPage)55601 static int pager_link_page(Pager *pPager,Page *pPage)
55602 {
55603 	sxu32 nBucket;
55604 	/* Install in the corresponding bucket */
55605 	nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
55606 	pPage->pNextCollide = pPager->apHash[nBucket];
55607 	if( pPager->apHash[nBucket] ){
55608 		pPager->apHash[nBucket]->pPrevCollide = pPage;
55609 	}
55610 	pPager->apHash[nBucket] = pPage;
55611 	/* Link to the list of active pages */
55612 	MACRO_LD_PUSH(pPager->pAll,pPage);
55613 	pPager->nPage++;
55614 	if( (pPager->nPage >= pPager->nSize * 4)  && pPager->nPage < 100000 ){
55615 		/* Grow the hashtable */
55616 		sxu32 nNewSize = pPager->nSize << 1;
55617 		Page *pEntry,**apNew;
55618 		sxu32 n;
55619 		apNew = (Page **)SyMemBackendAlloc(pPager->pAllocator, nNewSize * sizeof(Page *));
55620 		if( apNew ){
55621 			sxu32 iBucket;
55622 			/* Zero the new table */
55623 			SyZero((void *)apNew, nNewSize * sizeof(Page *));
55624 			/* Rehash all entries */
55625 			n = 0;
55626 			pEntry = pPager->pAll;
55627 			for(;;){
55628 				/* Loop one */
55629 				if( n >= pPager->nPage ){
55630 					break;
55631 				}
55632 				pEntry->pNextCollide = pEntry->pPrevCollide = 0;
55633 				/* Install in the new bucket */
55634 				iBucket = PAGE_HASH(pEntry->pgno) & (nNewSize - 1);
55635 				pEntry->pNextCollide = apNew[iBucket];
55636 				if( apNew[iBucket] ){
55637 					apNew[iBucket]->pPrevCollide = pEntry;
55638 				}
55639 				apNew[iBucket] = pEntry;
55640 				/* Point to the next entry */
55641 				pEntry = pEntry->pNext;
55642 				n++;
55643 			}
55644 			/* Release the old table and reflect the change */
55645 			SyMemBackendFree(pPager->pAllocator,(void *)pPager->apHash);
55646 			pPager->apHash = apNew;
55647 			pPager->nSize  = nNewSize;
55648 		}
55649 	}
55650 	return UNQLITE_OK;
55651 }
55652 /*
55653  * Unlink a page from the list of active pages.
55654  */
pager_unlink_page(Pager * pPager,Page * pPage)55655 static int pager_unlink_page(Pager *pPager,Page *pPage)
55656 {
55657 	if( pPage->pNextCollide ){
55658 		pPage->pNextCollide->pPrevCollide = pPage->pPrevCollide;
55659 	}
55660 	if( pPage->pPrevCollide ){
55661 		pPage->pPrevCollide->pNextCollide = pPage->pNextCollide;
55662 	}else{
55663 		sxu32 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
55664 		pPager->apHash[nBucket] = pPage->pNextCollide;
55665 	}
55666 	MACRO_LD_REMOVE(pPager->pAll,pPage);
55667 	pPager->nPage--;
55668 	return UNQLITE_OK;
55669 }
55670 /*
55671  * Update the content of a cached page.
55672  */
pager_fill_page(Pager * pPager,pgno iNum,void * pContents)55673 static int pager_fill_page(Pager *pPager,pgno iNum,void *pContents)
55674 {
55675 	Page *pPage;
55676 	/* Fetch the page from the catch */
55677 	pPage = pager_fetch_page(pPager,iNum);
55678 	if( pPage == 0 ){
55679 		return SXERR_NOTFOUND;
55680 	}
55681 	/* Reflect the change */
55682 	SyMemcpy(pContents,pPage->zData,pPager->iPageSize);
55683 
55684 	return UNQLITE_OK;
55685 }
55686 /*
55687  * Read the content of a page from disk.
55688  */
pager_get_page_contents(Pager * pPager,Page * pPage,int noContent)55689 static int pager_get_page_contents(Pager *pPager,Page *pPage,int noContent)
55690 {
55691 	int rc = UNQLITE_OK;
55692 	if( pPager->is_mem || noContent || pPage->pgno >= pPager->dbSize ){
55693 		/* Do not bother reading, zero the page contents only */
55694 		SyZero(pPage->zData,pPager->iPageSize);
55695 		return UNQLITE_OK;
55696 	}
55697 	if( (pPager->iOpenFlags & UNQLITE_OPEN_MMAP) && (pPager->pMmap /* Paranoid edition */) ){
55698 		unsigned char *zMap = (unsigned char *)pPager->pMmap;
55699 		pPage->zData = &zMap[pPage->pgno * pPager->iPageSize];
55700 	}else{
55701 		/* Read content */
55702 		rc = unqliteOsRead(pPager->pfd,pPage->zData,pPager->iPageSize,pPage->pgno * pPager->iPageSize);
55703 	}
55704 	return rc;
55705 }
55706 /*
55707  * Add a page to the dirty list.
55708  */
pager_page_to_dirty_list(Pager * pPager,Page * pPage)55709 static void pager_page_to_dirty_list(Pager *pPager,Page *pPage)
55710 {
55711 	if( pPage->flags & PAGE_DIRTY ){
55712 		/* Already set */
55713 		return;
55714 	}
55715 	/* Mark the page as dirty */
55716 	pPage->flags |= PAGE_DIRTY|PAGE_NEED_SYNC|PAGE_IN_JOURNAL;
55717 	/* Link to the list */
55718 	pPage->pDirtyPrev = 0;
55719 	pPage->pDirtyNext = pPager->pDirty;
55720 	if( pPager->pDirty ){
55721 		pPager->pDirty->pDirtyPrev = pPage;
55722 	}
55723 	pPager->pDirty = pPage;
55724 	if( pPager->pFirstDirty == 0 ){
55725 		pPager->pFirstDirty = pPage;
55726 	}
55727 }
55728 /*
55729  * Merge sort.
55730  * The merge sort implementation is based on the one used by
55731  * the PH7 Embeddable PHP Engine (http://ph7.symisc.net/).
55732  */
55733 /*
55734 ** Inputs:
55735 **   a:       A sorted, null-terminated linked list.  (May be null).
55736 **   b:       A sorted, null-terminated linked list.  (May be null).
55737 **   cmp:     A pointer to the comparison function.
55738 **
55739 ** Return Value:
55740 **   A pointer to the head of a sorted list containing the elements
55741 **   of both a and b.
55742 **
55743 ** Side effects:
55744 **   The "next", "prev" pointers for elements in the lists a and b are
55745 **   changed.
55746 */
page_merge_dirty(Page * pA,Page * pB)55747 static Page * page_merge_dirty(Page *pA, Page *pB)
55748 {
55749 	Page result, *pTail;
55750     /* Prevent compiler warning */
55751 	result.pDirtyNext = result.pDirtyPrev = 0;
55752 	pTail = &result;
55753 	while( pA && pB ){
55754 		if( pA->pgno < pB->pgno ){
55755 			pTail->pDirtyPrev = pA;
55756 			pA->pDirtyNext = pTail;
55757 			pTail = pA;
55758 			pA = pA->pDirtyPrev;
55759 		}else{
55760 			pTail->pDirtyPrev = pB;
55761 			pB->pDirtyNext = pTail;
55762 			pTail = pB;
55763 			pB = pB->pDirtyPrev;
55764 		}
55765 	}
55766 	if( pA ){
55767 		pTail->pDirtyPrev = pA;
55768 		pA->pDirtyNext = pTail;
55769 	}else if( pB ){
55770 		pTail->pDirtyPrev = pB;
55771 		pB->pDirtyNext = pTail;
55772 	}else{
55773 		pTail->pDirtyPrev = pTail->pDirtyNext = 0;
55774 	}
55775 	return result.pDirtyPrev;
55776 }
55777 /*
55778 ** Inputs:
55779 **   Map:       Input hashmap
55780 **   cmp:       A comparison function.
55781 **
55782 ** Return Value:
55783 **   Sorted hashmap.
55784 **
55785 ** Side effects:
55786 **   The "next" pointers for elements in list are changed.
55787 */
55788 #define N_SORT_BUCKET  32
pager_get_dirty_pages(Pager * pPager)55789 static Page * pager_get_dirty_pages(Pager *pPager)
55790 {
55791 	Page *a[N_SORT_BUCKET], *p, *pIn;
55792 	sxu32 i;
55793 	if( pPager->pFirstDirty == 0 ){
55794 		/* Don't bother sorting, the list is already empty */
55795 		return 0;
55796 	}
55797 	SyZero(a, sizeof(a));
55798 	/* Point to the first inserted entry */
55799 	pIn = pPager->pFirstDirty;
55800 	while( pIn ){
55801 		p = pIn;
55802 		pIn = p->pDirtyPrev;
55803 		p->pDirtyPrev = 0;
55804 		for(i=0; i<N_SORT_BUCKET-1; i++){
55805 			if( a[i]==0 ){
55806 				a[i] = p;
55807 				break;
55808 			}else{
55809 				p = page_merge_dirty(a[i], p);
55810 				a[i] = 0;
55811 			}
55812 		}
55813 		if( i==N_SORT_BUCKET-1 ){
55814 			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
55815 			 * But that is impossible.
55816 			 */
55817 			a[i] = page_merge_dirty(a[i], p);
55818 		}
55819 	}
55820 	p = a[0];
55821 	for(i=1; i<N_SORT_BUCKET; i++){
55822 		p = page_merge_dirty(p,a[i]);
55823 	}
55824 	p->pDirtyNext = 0;
55825 	return p;
55826 }
55827 /*
55828  * See block comment above.
55829  */
page_merge_hot(Page * pA,Page * pB)55830 static Page * page_merge_hot(Page *pA, Page *pB)
55831 {
55832 	Page result, *pTail;
55833     /* Prevent compiler warning */
55834 	result.pNextHot = result.pPrevHot = 0;
55835 	pTail = &result;
55836 	while( pA && pB ){
55837 		if( pA->pgno < pB->pgno ){
55838 			pTail->pPrevHot = pA;
55839 			pA->pNextHot = pTail;
55840 			pTail = pA;
55841 			pA = pA->pPrevHot;
55842 		}else{
55843 			pTail->pPrevHot = pB;
55844 			pB->pNextHot = pTail;
55845 			pTail = pB;
55846 			pB = pB->pPrevHot;
55847 		}
55848 	}
55849 	if( pA ){
55850 		pTail->pPrevHot = pA;
55851 		pA->pNextHot = pTail;
55852 	}else if( pB ){
55853 		pTail->pPrevHot = pB;
55854 		pB->pNextHot = pTail;
55855 	}else{
55856 		pTail->pPrevHot = pTail->pNextHot = 0;
55857 	}
55858 	return result.pPrevHot;
55859 }
55860 /*
55861 ** Inputs:
55862 **   Map:       Input hashmap
55863 **   cmp:       A comparison function.
55864 **
55865 ** Return Value:
55866 **   Sorted hashmap.
55867 **
55868 ** Side effects:
55869 **   The "next" pointers for elements in list are changed.
55870 */
55871 #define N_SORT_BUCKET  32
pager_get_hot_pages(Pager * pPager)55872 static Page * pager_get_hot_pages(Pager *pPager)
55873 {
55874 	Page *a[N_SORT_BUCKET], *p, *pIn;
55875 	sxu32 i;
55876 	if( pPager->pFirstHot == 0 ){
55877 		/* Don't bother sorting, the list is already empty */
55878 		return 0;
55879 	}
55880 	SyZero(a, sizeof(a));
55881 	/* Point to the first inserted entry */
55882 	pIn = pPager->pFirstHot;
55883 	while( pIn ){
55884 		p = pIn;
55885 		pIn = p->pPrevHot;
55886 		p->pPrevHot = 0;
55887 		for(i=0; i<N_SORT_BUCKET-1; i++){
55888 			if( a[i]==0 ){
55889 				a[i] = p;
55890 				break;
55891 			}else{
55892 				p = page_merge_hot(a[i], p);
55893 				a[i] = 0;
55894 			}
55895 		}
55896 		if( i==N_SORT_BUCKET-1 ){
55897 			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
55898 			 * But that is impossible.
55899 			 */
55900 			a[i] = page_merge_hot(a[i], p);
55901 		}
55902 	}
55903 	p = a[0];
55904 	for(i=1; i<N_SORT_BUCKET; i++){
55905 		p = page_merge_hot(p,a[i]);
55906 	}
55907 	p->pNextHot = 0;
55908 	return p;
55909 }
55910 /*
55911 ** The format for the journal header is as follows:
55912 ** - 8 bytes: Magic identifying journal format.
55913 ** - 4 bytes: Number of records in journal.
55914 ** - 4 bytes: Random number used for page hash.
55915 ** - 8 bytes: Initial database page count.
55916 ** - 4 bytes: Sector size used by the process that wrote this journal.
55917 ** - 4 bytes: Database page size.
55918 **
55919 ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
55920 */
55921 /*
55922 ** Open the journal file and extract its header information.
55923 **
55924 ** If the header is read successfully, *pNRec is set to the number of
55925 ** page records following this header and *pDbSize is set to the size of the
55926 ** database before the transaction began, in pages. Also, pPager->cksumInit
55927 ** is set to the value read from the journal header. UNQLITE_OK is returned
55928 ** in this case.
55929 **
55930 ** If the journal header file appears to be corrupted, UNQLITE_DONE is
55931 ** returned and *pNRec and *PDbSize are undefined.  If JOURNAL_HDR_SZ bytes
55932 ** cannot be read from the journal file an error code is returned.
55933 */
pager_read_journal_header(Pager * pPager,sxu32 * pNRec,pgno * pDbSize)55934 static int pager_read_journal_header(
55935   Pager *pPager,               /* Pager object */
55936   sxu32 *pNRec,                /* OUT: Value read from the nRec field */
55937   pgno  *pDbSize               /* OUT: Value of original database size field */
55938 )
55939 {
55940 	sxu32 iPageSize,iSectorSize;
55941 	unsigned char zMagic[8];
55942 	sxi64 iHdrOfft;
55943 	sxi64 iSize;
55944 	int rc;
55945 	/* Offset to start reading from */
55946 	iHdrOfft = 0;
55947 	/* Get the size of the journal */
55948 	rc = unqliteOsFileSize(pPager->pjfd,&iSize);
55949 	if( rc != UNQLITE_OK ){
55950 		return UNQLITE_DONE;
55951 	}
55952 	/* If the journal file is too small, return UNQLITE_DONE. */
55953 	if( 32 /* Minimum sector size */> iSize ){
55954 		return UNQLITE_DONE;
55955 	}
55956 	/* Make sure we are dealing with a valid journal */
55957 	rc = unqliteOsRead(pPager->pjfd,zMagic,sizeof(zMagic),iHdrOfft);
55958 	if( rc != UNQLITE_OK ){
55959 		return rc;
55960 	}
55961 	if( SyMemcmp(zMagic,aJournalMagic,sizeof(zMagic)) != 0 ){
55962 		return UNQLITE_DONE;
55963 	}
55964 	iHdrOfft += sizeof(zMagic);
55965 	 /* Read the first three 32-bit fields of the journal header: The nRec
55966       ** field, the checksum-initializer and the database size at the start
55967       ** of the transaction. Return an error code if anything goes wrong.
55968       */
55969 	rc = ReadInt32(pPager->pjfd,pNRec,iHdrOfft);
55970 	if( rc != UNQLITE_OK ){
55971 		return rc;
55972 	}
55973 	iHdrOfft += 4;
55974 	rc = ReadInt32(pPager->pjfd,&pPager->cksumInit,iHdrOfft);
55975 	if( rc != UNQLITE_OK ){
55976 		return rc;
55977 	}
55978 	iHdrOfft += 4;
55979 	rc = ReadInt64(pPager->pjfd,pDbSize,iHdrOfft);
55980 	if( rc != UNQLITE_OK ){
55981 		return rc;
55982 	}
55983 	iHdrOfft += 8;
55984 	/* Read the page-size and sector-size journal header fields. */
55985 	rc = ReadInt32(pPager->pjfd,&iSectorSize,iHdrOfft);
55986 	if( rc != UNQLITE_OK ){
55987 		return rc;
55988 	}
55989 	iHdrOfft += 4;
55990 	rc = ReadInt32(pPager->pjfd,&iPageSize,iHdrOfft);
55991 	if( rc != UNQLITE_OK ){
55992 		return rc;
55993 	}
55994 	/* Check that the values read from the page-size and sector-size fields
55995     ** are within range. To be 'in range', both values need to be a power
55996     ** of two greater than or equal to 512 or 32, and not greater than their
55997     ** respective compile time maximum limits.
55998     */
55999     if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32
56000      || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE
56001      || ((iPageSize-1)&iPageSize)!=0    || ((iSectorSize-1)&iSectorSize)!=0
56002     ){
56003       /* If the either the page-size or sector-size in the journal-header is
56004       ** invalid, then the process that wrote the journal-header must have
56005       ** crashed before the header was synced. In this case stop reading
56006       ** the journal file here.
56007       */
56008       return UNQLITE_DONE;
56009     }
56010     /* Update the assumed sector-size to match the value used by
56011     ** the process that created this journal. If this journal was
56012     ** created by a process other than this one, then this routine
56013     ** is being called from within pager_playback(). The local value
56014     ** of Pager.sectorSize is restored at the end of that routine.
56015     */
56016     pPager->iSectorSize = iSectorSize;
56017 	pPager->iPageSize = iPageSize;
56018 	/* Ready to rollback */
56019 	pPager->iJournalOfft = JOURNAL_HDR_SZ(pPager);
56020 	/* All done */
56021 	return UNQLITE_OK;
56022 }
56023 /*
56024  * Write the journal header in the given memory buffer.
56025  * The given buffer is big enough to hold the whole header.
56026  */
pager_write_journal_header(Pager * pPager,unsigned char * zBuf)56027 static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf)
56028 {
56029 	unsigned char *zPtr = zBuf;
56030 	/* 8 bytes magic number */
56031 	SyMemcpy(aJournalMagic,zPtr,sizeof(aJournalMagic));
56032 	zPtr += sizeof(aJournalMagic);
56033 	/* 4 bytes: Number of records in journal. */
56034 	SyBigEndianPack32(zPtr,0);
56035 	zPtr += 4;
56036 	/* 4 bytes: Random number used to compute page checksum. */
56037 	SyBigEndianPack32(zPtr,pPager->cksumInit);
56038 	zPtr += 4;
56039 	/* 8 bytes: Initial database page count. */
56040 	SyBigEndianPack64(zPtr,pPager->dbOrigSize);
56041 	zPtr += 8;
56042 	/* 4 bytes: Sector size used by the process that wrote this journal. */
56043 	SyBigEndianPack32(zPtr,(sxu32)pPager->iSectorSize);
56044 	zPtr += 4;
56045 	/* 4 bytes: Database page size. */
56046 	SyBigEndianPack32(zPtr,(sxu32)pPager->iPageSize);
56047 	return UNQLITE_OK;
56048 }
56049 /*
56050 ** Parameter aData must point to a buffer of pPager->pageSize bytes
56051 ** of data. Compute and return a checksum based ont the contents of the
56052 ** page of data and the current value of pPager->cksumInit.
56053 **
56054 ** This is not a real checksum. It is really just the sum of the
56055 ** random initial value (pPager->cksumInit) and every 200th byte
56056 ** of the page data, starting with byte offset (pPager->pageSize%200).
56057 ** Each byte is interpreted as an 8-bit unsigned integer.
56058 **
56059 ** Changing the formula used to compute this checksum results in an
56060 ** incompatible journal file format.
56061 **
56062 ** If journal corruption occurs due to a power failure, the most likely
56063 ** scenario is that one end or the other of the record will be changed.
56064 ** It is much less likely that the two ends of the journal record will be
56065 ** correct and the middle be corrupt.  Thus, this "checksum" scheme,
56066 ** though fast and simple, catches the mostly likely kind of corruption.
56067 */
pager_cksum(Pager * pPager,const unsigned char * zData)56068 static sxu32 pager_cksum(Pager *pPager,const unsigned char *zData)
56069 {
56070   sxu32 cksum = pPager->cksumInit;         /* Checksum value to return */
56071   int i = pPager->iPageSize-200;          /* Loop counter */
56072   while( i>0 ){
56073     cksum += zData[i];
56074     i -= 200;
56075   }
56076   return cksum;
56077 }
56078 /*
56079 ** Read a single page from the journal file opened on file descriptor
56080 ** jfd. Playback this one page. Update the offset to read from.
56081 */
pager_play_back_one_page(Pager * pPager,sxi64 * pOfft,unsigned char * zTmp)56082 static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zTmp)
56083 {
56084 	unsigned char *zData = zTmp;
56085 	sxi64 iOfft; /* Offset to read from */
56086 	pgno iNum;   /* Pager number */
56087 	sxu32 ckSum; /* Sanity check */
56088 	int rc;
56089 	/* Offset to start reading from */
56090 	iOfft = *pOfft;
56091 	/* Database page number */
56092 	rc = ReadInt64(pPager->pjfd,&iNum,iOfft);
56093 	if( rc != UNQLITE_OK ){ return rc; }
56094 	iOfft += 8;
56095 	/* Page data */
56096 	rc = unqliteOsRead(pPager->pjfd,zData,pPager->iPageSize,iOfft);
56097 	if( rc != UNQLITE_OK ){ return rc; }
56098 	iOfft += pPager->iPageSize;
56099 	/* Page cksum */
56100 	rc = ReadInt32(pPager->pjfd,&ckSum,iOfft);
56101 	if( rc != UNQLITE_OK ){ return rc; }
56102 	iOfft += 4;
56103 	/* Synchronize pointers */
56104 	*pOfft = iOfft;
56105 	/* Make sure we are dealing with a valid page */
56106 	if( ckSum != pager_cksum(pPager,zData) ){
56107 		/* Ignore that page */
56108 		return SXERR_IGNORE;
56109 	}
56110 	if( iNum >= pPager->dbSize ){
56111 		/* Ignore that page */
56112 		return UNQLITE_OK;
56113 	}
56114 	/* playback */
56115 	rc = unqliteOsWrite(pPager->pfd,zData,pPager->iPageSize,iNum * pPager->iPageSize);
56116 	if( rc == UNQLITE_OK ){
56117 		/* Flush the cache */
56118 		pager_fill_page(pPager,iNum,zData);
56119 	}
56120 	return rc;
56121 }
56122 /*
56123 ** Playback the journal and thus restore the database file to
56124 ** the state it was in before we started making changes.
56125 **
56126 ** The journal file format is as follows:
56127 **
56128 **  (1)  8 byte prefix.  A copy of aJournalMagic[].
56129 **  (2)  4 byte big-endian integer which is the number of valid page records
56130 **       in the journal.
56131 **  (3)  4 byte big-endian integer which is the initial value for the
56132 **       sanity checksum.
56133 **  (4)  8 byte integer which is the number of pages to truncate the
56134 **       database to during a rollback.
56135 **  (5)  4 byte big-endian integer which is the sector size.  The header
56136 **       is this many bytes in size.
56137 **  (6)  4 byte big-endian integer which is the page size.
56138 **  (7)  zero padding out to the next sector size.
56139 **  (8)  Zero or more pages instances, each as follows:
56140 **        +  4 byte page number.
56141 **        +  pPager->pageSize bytes of data.
56142 **        +  4 byte checksum
56143 **
56144 ** When we speak of the journal header, we mean the first 7 items above.
56145 ** Each entry in the journal is an instance of the 8th item.
56146 **
56147 ** Call the value from the second bullet "nRec".  nRec is the number of
56148 ** valid page entries in the journal.  In most cases, you can compute the
56149 ** value of nRec from the size of the journal file.  But if a power
56150 ** failure occurred while the journal was being written, it could be the
56151 ** case that the size of the journal file had already been increased but
56152 ** the extra entries had not yet made it safely to disk.  In such a case,
56153 ** the value of nRec computed from the file size would be too large.  For
56154 ** that reason, we always use the nRec value in the header.
56155 **
56156 ** If the file opened as the journal file is not a well-formed
56157 ** journal file then all pages up to the first corrupted page are rolled
56158 ** back (or no pages if the journal header is corrupted). The journal file
56159 ** is then deleted and SQLITE_OK returned, just as if no corruption had
56160 ** been encountered.
56161 **
56162 ** If an I/O or malloc() error occurs, the journal-file is not deleted
56163 ** and an error code is returned.
56164 **
56165 */
pager_playback(Pager * pPager)56166 static int pager_playback(Pager *pPager)
56167 {
56168 	unsigned char *zTmp = 0; /* cc warning */
56169 	sxu32 n,nRec;
56170 	sxi64 iOfft;
56171 	int rc;
56172 	/* Read the journal header*/
56173 	rc = pager_read_journal_header(pPager,&nRec,&pPager->dbSize);
56174 	if( rc != UNQLITE_OK ){
56175 		if( rc == UNQLITE_DONE ){
56176 			goto end_playback;
56177 		}
56178 		unqliteGenErrorFormat(pPager->pDb,"IO error while reading journal file '%s' header",pPager->zJournal);
56179 		return rc;
56180 	}
56181 	/* Truncate the database back to its original size */
56182 	rc = unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
56183 	if( rc != UNQLITE_OK ){
56184 		unqliteGenError(pPager->pDb,"IO error while truncating database file");
56185 		return rc;
56186 	}
56187 	/* Allocate a temporary page */
56188 	zTmp = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
56189 	if( zTmp == 0 ){
56190 		unqliteGenOutofMem(pPager->pDb);
56191 		return UNQLITE_NOMEM;
56192 	}
56193 	SyZero((void *)zTmp,(sxu32)pPager->iPageSize);
56194 	/* Copy original pages out of the journal and back into the
56195     ** database file and/or page cache.
56196     */
56197 	iOfft = pPager->iJournalOfft;
56198 	for( n = 0 ; n < nRec ; ++n ){
56199 		rc = pager_play_back_one_page(pPager,&iOfft,zTmp);
56200 		if( rc != UNQLITE_OK ){
56201 			if( rc != SXERR_IGNORE ){
56202 				unqliteGenError(pPager->pDb,"Page playback error");
56203 				goto end_playback;
56204 			}
56205 		}
56206 	}
56207 end_playback:
56208 	/* Release the temp page */
56209 	SyMemBackendFree(pPager->pAllocator,(void *)zTmp);
56210 	if( rc == UNQLITE_OK ){
56211 		/* Sync the database file */
56212 		unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
56213 	}
56214 	if( rc == UNQLITE_DONE ){
56215 		rc = UNQLITE_OK;
56216 	}
56217 	/* Return to the caller */
56218 	return rc;
56219 }
56220 /*
56221 ** Unlock the database file to level eLock, which must be either NO_LOCK
56222 ** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
56223 ** succeeds, set the Pager.iLock variable to match the (attempted) new lock.
56224 **
56225 ** Except, if Pager.iLock is set to NO_LOCK when this function is
56226 ** called, do not modify it. See the comment above the #define of
56227 ** NO_LOCK for an explanation of this.
56228 */
pager_unlock_db(Pager * pPager,int eLock)56229 static int pager_unlock_db(Pager *pPager, int eLock)
56230 {
56231   int rc = UNQLITE_OK;
56232   if( pPager->iLock != NO_LOCK ){
56233     rc = unqliteOsUnlock(pPager->pfd,eLock);
56234     pPager->iLock = eLock;
56235   }
56236   return rc;
56237 }
56238 /*
56239 ** Lock the database file to level eLock, which must be either SHARED_LOCK,
56240 ** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
56241 ** Pager.eLock variable to the new locking state.
56242 **
56243 ** Except, if Pager.eLock is set to NO_LOCK when this function is
56244 ** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
56245 ** See the comment above the #define of NO_LOCK for an explanation
56246 ** of this.
56247 */
pager_lock_db(Pager * pPager,int eLock)56248 static int pager_lock_db(Pager *pPager, int eLock){
56249   int rc = UNQLITE_OK;
56250   if( pPager->iLock < eLock || pPager->iLock == NO_LOCK ){
56251     rc = unqliteOsLock(pPager->pfd, eLock);
56252     if( rc==UNQLITE_OK ){
56253       pPager->iLock = eLock;
56254     }else{
56255 		unqliteGenError(pPager->pDb,
56256 			rc == UNQLITE_BUSY ? "Another process or thread hold the requested lock" : "Error while requesting database lock"
56257 			);
56258 	}
56259   }
56260   return rc;
56261 }
56262 /*
56263 ** Try to obtain a lock of type locktype on the database file. If
56264 ** a similar or greater lock is already held, this function is a no-op
56265 ** (returning UNQLITE_OK immediately).
56266 **
56267 ** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke
56268 ** the busy callback if the lock is currently not available. Repeat
56269 ** until the busy callback returns false or until the attempt to
56270 ** obtain the lock succeeds.
56271 **
56272 ** Return UNQLITE_OK on success and an error code if we cannot obtain
56273 ** the lock. If the lock is obtained successfully, set the Pager.state
56274 ** variable to locktype before returning.
56275 */
pager_wait_on_lock(Pager * pPager,int locktype)56276 static int pager_wait_on_lock(Pager *pPager, int locktype){
56277   int rc;                              /* Return code */
56278   do {
56279     rc = pager_lock_db(pPager,locktype);
56280   }while( rc==UNQLITE_BUSY && pPager->xBusyHandler && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
56281   return rc;
56282 }
56283 /*
56284 ** This function is called after transitioning from PAGER_OPEN to
56285 ** PAGER_SHARED state. It tests if there is a hot journal present in
56286 ** the file-system for the given pager. A hot journal is one that
56287 ** needs to be played back. According to this function, a hot-journal
56288 ** file exists if the following criteria are met:
56289 **
56290 **   * The journal file exists in the file system, and
56291 **   * No process holds a RESERVED or greater lock on the database file, and
56292 **   * The database file itself is greater than 0 bytes in size, and
56293 **   * The first byte of the journal file exists and is not 0x00.
56294 **
56295 ** If the current size of the database file is 0 but a journal file
56296 ** exists, that is probably an old journal left over from a prior
56297 ** database with the same name. In this case the journal file is
56298 ** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK
56299 ** is returned.
56300 **
56301 ** If a hot-journal file is found to exist, *pExists is set to 1 and
56302 ** UNQLITE_OK returned. If no hot-journal file is present, *pExists is
56303 ** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying
56304 ** to determine whether or not a hot-journal file exists, the IO error
56305 ** code is returned and the value of *pExists is undefined.
56306 */
pager_has_hot_journal(Pager * pPager,int * pExists)56307 static int pager_has_hot_journal(Pager *pPager, int *pExists)
56308 {
56309   unqlite_vfs *pVfs = pPager->pVfs;
56310   int rc = UNQLITE_OK;           /* Return code */
56311   int exists = 1;               /* True if a journal file is present */
56312 
56313   *pExists = 0;
56314   rc = unqliteOsAccess(pVfs, pPager->zJournal, UNQLITE_ACCESS_EXISTS, &exists);
56315   if( rc==UNQLITE_OK && exists ){
56316     int locked = 0;             /* True if some process holds a RESERVED lock */
56317 
56318     /* Race condition here:  Another process might have been holding the
56319     ** the RESERVED lock and have a journal open at the unqliteOsAccess()
56320     ** call above, but then delete the journal and drop the lock before
56321     ** we get to the following unqliteOsCheckReservedLock() call.  If that
56322     ** is the case, this routine might think there is a hot journal when
56323     ** in fact there is none.  This results in a false-positive which will
56324     ** be dealt with by the playback routine.
56325     */
56326     rc = unqliteOsCheckReservedLock(pPager->pfd, &locked);
56327     if( rc==UNQLITE_OK && !locked ){
56328       sxi64 n = 0;                    /* Size of db file in bytes */
56329 
56330       /* Check the size of the database file. If it consists of 0 pages,
56331       ** then delete the journal file. See the header comment above for
56332       ** the reasoning here.  Delete the obsolete journal file under
56333       ** a RESERVED lock to avoid race conditions.
56334       */
56335       rc = unqliteOsFileSize(pPager->pfd,&n);
56336       if( rc==UNQLITE_OK ){
56337         if( n < 1 ){
56338           if( pager_lock_db(pPager, RESERVED_LOCK)==UNQLITE_OK ){
56339             unqliteOsDelete(pVfs, pPager->zJournal, 0);
56340 			pager_unlock_db(pPager, SHARED_LOCK);
56341           }
56342         }else{
56343           /* The journal file exists and no other connection has a reserved
56344           ** or greater lock on the database file. */
56345 			*pExists = 1;
56346         }
56347       }
56348     }
56349   }
56350   return rc;
56351 }
56352 /*
56353  * Rollback a journal file. (See block-comment above).
56354  */
pager_journal_rollback(Pager * pPager,int check_hot)56355 static int pager_journal_rollback(Pager *pPager,int check_hot)
56356 {
56357 	int rc;
56358 	if( check_hot ){
56359 		int iExists = 0; /* cc warning */
56360 		/* Check if the journal file exists */
56361 		rc = pager_has_hot_journal(pPager,&iExists);
56362 		if( rc != UNQLITE_OK  ){
56363 			/* IO error */
56364 			return rc;
56365 		}
56366 		if( !iExists ){
56367 			/* Journal file does not exists */
56368 			return UNQLITE_OK;
56369 		}
56370 	}
56371 	if( pPager->is_rdonly ){
56372 		unqliteGenErrorFormat(pPager->pDb,
56373 			"Cannot rollback journal file '%s' due to a read-only database handle",pPager->zJournal);
56374 		return UNQLITE_READ_ONLY;
56375 	}
56376 	/* Get an EXCLUSIVE lock on the database file. At this point it is
56377       ** important that a RESERVED lock is not obtained on the way to the
56378       ** EXCLUSIVE lock. If it were, another process might open the
56379       ** database file, detect the RESERVED lock, and conclude that the
56380       ** database is safe to read while this process is still rolling the
56381       ** hot-journal back.
56382       **
56383       ** Because the intermediate RESERVED lock is not requested, any
56384       ** other process attempting to access the database file will get to
56385       ** this point in the code and fail to obtain its own EXCLUSIVE lock
56386       ** on the database file.
56387       **
56388       ** Unless the pager is in locking_mode=exclusive mode, the lock is
56389       ** downgraded to SHARED_LOCK before this function returns.
56390       */
56391 	/* Open the journal file */
56392 	rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,&pPager->pjfd,UNQLITE_OPEN_READWRITE);
56393 	if( rc != UNQLITE_OK ){
56394 		unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: '%s'",pPager->zJournal);
56395 		goto fail;
56396 	}
56397 	rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
56398 	if( rc != UNQLITE_OK ){
56399 		unqliteGenError(pPager->pDb,"Cannot acquire an exclusive lock on the database while journal rollback");
56400 		goto fail;
56401 	}
56402 	/* Sync the journal file */
56403 	unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
56404 	/* Finally rollback the database */
56405 	rc = pager_playback(pPager);
56406 	/* Switch back to shared lock */
56407 	pager_unlock_db(pPager,SHARED_LOCK);
56408 fail:
56409 	/* Close the journal handle */
56410 	unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
56411 	pPager->pjfd = 0;
56412 	if( rc == UNQLITE_OK ){
56413 		/* Delete the journal file */
56414 		unqliteOsDelete(pPager->pVfs,pPager->zJournal,TRUE);
56415 	}
56416 	return rc;
56417 }
56418 /*
56419  * Write the unqlite header (First page). (Big-Endian)
56420  */
pager_write_db_header(Pager * pPager)56421 static int pager_write_db_header(Pager *pPager)
56422 {
56423 	unsigned char *zRaw = pPager->pHeader->zData;
56424 	unqlite_kv_engine *pEngine = pPager->pEngine;
56425 	sxu32 nDos;
56426 	sxu16 nLen;
56427 	/* Database signature */
56428 	SyMemcpy(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1);
56429 	zRaw += sizeof(UNQLITE_DB_SIG)-1;
56430 	/* Database magic number */
56431 	SyBigEndianPack32(zRaw,UNQLITE_DB_MAGIC);
56432 	zRaw += 4; /* 4 byte magic number */
56433 	/* Database creation time */
56434 	SyZero(&pPager->tmCreate,sizeof(Sytm));
56435 	if( pPager->pVfs->xCurrentTime ){
56436 		pPager->pVfs->xCurrentTime(pPager->pVfs,&pPager->tmCreate);
56437 	}
56438 	/* DOS time format (4 bytes) */
56439 	SyTimeFormatToDos(&pPager->tmCreate,&nDos);
56440 	SyBigEndianPack32(zRaw,nDos);
56441 	zRaw += 4; /* 4 byte DOS time */
56442 	/* Sector size */
56443 	SyBigEndianPack32(zRaw,(sxu32)pPager->iSectorSize);
56444 	zRaw += 4; /* 4 byte sector size */
56445 	/* Page size */
56446 	SyBigEndianPack32(zRaw,(sxu32)pPager->iPageSize);
56447 	zRaw += 4; /* 4 byte page size */
56448 	/* Key value storage engine */
56449 	nLen = (sxu16)SyStrlen(pEngine->pIo->pMethods->zName);
56450 	SyBigEndianPack16(zRaw,nLen); /* 2 byte storage engine name */
56451 	zRaw += 2;
56452 	SyMemcpy((const void *)pEngine->pIo->pMethods->zName,(void *)zRaw,nLen);
56453 	zRaw += nLen;
56454 	/* All rest are meta-data available to the host application */
56455 	return UNQLITE_OK;
56456 }
56457 /*
56458  * Read the unqlite header (first page). (Big-Endian)
56459  */
pager_extract_header(Pager * pPager,const unsigned char * zRaw,sxu32 nByte)56460 static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nByte)
56461 {
56462 	const unsigned char *zEnd = &zRaw[nByte];
56463 	sxu32 nDos,iMagic;
56464 	sxu16 nLen;
56465 	char *zKv;
56466 	/* Database signature */
56467 	if( SyMemcmp(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1) != 0 ){
56468 		/* Corrupt database */
56469 		return UNQLITE_CORRUPT;
56470 	}
56471 	zRaw += sizeof(UNQLITE_DB_SIG)-1;
56472 	/* Database magic number */
56473 	SyBigEndianUnpack32(zRaw,&iMagic);
56474 	zRaw += 4; /* 4 byte magic number */
56475 	if( iMagic != UNQLITE_DB_MAGIC ){
56476 		/* Corrupt database */
56477 		return UNQLITE_CORRUPT;
56478 	}
56479 	/* Database creation time */
56480 	SyBigEndianUnpack32(zRaw,&nDos);
56481 	zRaw += 4; /* 4 byte DOS time format */
56482 	SyDosTimeFormat(nDos,&pPager->tmCreate);
56483 	/* Sector size */
56484 	SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iSectorSize);
56485 	zRaw += 4; /* 4 byte sector size */
56486 	/* Page size */
56487 	SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iPageSize);
56488 	zRaw += 4; /* 4 byte page size */
56489 	/* Check that the values read from the page-size and sector-size fields
56490     ** are within range. To be 'in range', both values need to be a power
56491     ** of two greater than or equal to 512 or 32, and not greater than their
56492     ** respective compile time maximum limits.
56493     */
56494     if( pPager->iPageSize<UNQLITE_MIN_PAGE_SIZE || pPager->iSectorSize<32
56495      || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE
56496      || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0    || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0
56497     ){
56498       return UNQLITE_CORRUPT;
56499 	}
56500 	/* Key value storage engine */
56501 	SyBigEndianUnpack16(zRaw,&nLen); /* 2 byte storage engine length */
56502 	zRaw += 2;
56503 	if( nLen > (sxu16)(zEnd - zRaw) ){
56504 		nLen = (sxu16)(zEnd - zRaw);
56505 	}
56506 	zKv = (char *)SyMemBackendDup(pPager->pAllocator,(const char *)zRaw,nLen);
56507 	if( zKv == 0 ){
56508 		return UNQLITE_NOMEM;
56509 	}
56510 	SyStringInitFromBuf(&pPager->sKv,zKv,nLen);
56511 	return UNQLITE_OK;
56512 }
56513 /*
56514  * Read the database header.
56515  */
pager_read_db_header(Pager * pPager)56516 static int pager_read_db_header(Pager *pPager)
56517 {
56518 	unsigned char zRaw[UNQLITE_MIN_PAGE_SIZE]; /* Minimum page size */
56519 	sxi64 n = 0;              /* Size of db file in bytes */
56520 	int rc;
56521 	/* Get the file size first */
56522 	rc = unqliteOsFileSize(pPager->pfd,&n);
56523 	if( rc != UNQLITE_OK ){
56524 		return rc;
56525 	}
56526 	pPager->dbByteSize = n;
56527 	if( n > 0 ){
56528 		unqlite_kv_methods *pMethods;
56529 		SyString *pKv;
56530 		pgno nPage;
56531 		if( n < UNQLITE_MIN_PAGE_SIZE ){
56532 			/* A valid unqlite database must be at least 512 bytes long */
56533 			unqliteGenError(pPager->pDb,"Malformed database image");
56534 			return UNQLITE_CORRUPT;
56535 		}
56536 		/* Read the database header */
56537 		rc = unqliteOsRead(pPager->pfd,zRaw,sizeof(zRaw),0);
56538 		if( rc != UNQLITE_OK ){
56539 			unqliteGenError(pPager->pDb,"IO error while reading database header");
56540 			return rc;
56541 		}
56542 		/* Extract the header */
56543 		rc = pager_extract_header(pPager,zRaw,sizeof(zRaw));
56544 		if( rc != UNQLITE_OK ){
56545 			unqliteGenError(pPager->pDb,rc == UNQLITE_NOMEM ? "Unqlite is running out of memory" : "Malformed database image");
56546 			return rc;
56547 		}
56548 		/* Update pager state  */
56549 		nPage = (pgno)(n / pPager->iPageSize);
56550 		if( nPage==0 && n>0 ){
56551 			nPage = 1;
56552 		}
56553 		pPager->dbSize = nPage;
56554 		/* Laod the target Key/Value storage engine */
56555 		pKv = &pPager->sKv;
56556 		pMethods = unqliteFindKVStore(pKv->zString,pKv->nByte);
56557 		if( pMethods == 0 ){
56558 			unqliteGenErrorFormat(pPager->pDb,"No such Key/Value storage engine '%z'",pKv);
56559 			return UNQLITE_NOTIMPLEMENTED;
56560 		}
56561 		/* Install the new KV storage engine */
56562 		rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
56563 		if( rc != UNQLITE_OK ){
56564 			return rc;
56565 		}
56566 	}else{
56567 		/* Set a default page and sector size */
56568 		pPager->iSectorSize = GetSectorSize(pPager->pfd);
56569 		pPager->iPageSize = unqliteGetPageSize();
56570 		SyStringInitFromBuf(&pPager->sKv,pPager->pEngine->pIo->pMethods->zName,SyStrlen(pPager->pEngine->pIo->pMethods->zName));
56571 		pPager->dbSize = 0;
56572 	}
56573 	/* Allocate a temporary page size */
56574 	pPager->zTmpPage = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
56575 	if( pPager->zTmpPage == 0 ){
56576 		unqliteGenOutofMem(pPager->pDb);
56577 		return UNQLITE_NOMEM;
56578 	}
56579 	SyZero(pPager->zTmpPage,(sxu32)pPager->iPageSize);
56580 	return UNQLITE_OK;
56581 }
56582 /*
56583  * Write the database header.
56584  */
pager_create_header(Pager * pPager)56585 static int pager_create_header(Pager *pPager)
56586 {
56587 	Page *pHeader;
56588 	int rc;
56589 	/* Allocate a new page */
56590 	pHeader = pager_alloc_page(pPager,0);
56591 	if( pHeader == 0 ){
56592 		return UNQLITE_NOMEM;
56593 	}
56594 	pPager->pHeader = pHeader;
56595 	/* Link the page */
56596 	pager_link_page(pPager,pHeader);
56597 	/* Add to the dirty list */
56598 	pager_page_to_dirty_list(pPager,pHeader);
56599 	/* Write the database header */
56600 	rc = pager_write_db_header(pPager);
56601 	return rc;
56602 }
56603 /*
56604 ** This function is called to obtain a shared lock on the database file.
56605 ** It is illegal to call unqlitePagerAcquire() until after this function
56606 ** has been successfully called. If a shared-lock is already held when
56607 ** this function is called, it is a no-op.
56608 **
56609 ** The following operations are also performed by this function.
56610 **
56611 **   1) If the pager is currently in PAGER_OPEN state (no lock held
56612 **      on the database file), then an attempt is made to obtain a
56613 **      SHARED lock on the database file. Immediately after obtaining
56614 **      the SHARED lock, the file-system is checked for a hot-journal,
56615 **      which is played back if present.
56616 **
56617 ** If everything is successful, UNQLITE_OK is returned. If an IO error
56618 ** occurs while locking the database, checking for a hot-journal file or
56619 ** rolling back a journal file, the IO error code is returned.
56620 */
pager_shared_lock(Pager * pPager)56621 static int pager_shared_lock(Pager *pPager)
56622 {
56623 	int rc = UNQLITE_OK;
56624 	if( pPager->iState == PAGER_OPEN ){
56625 		unqlite_kv_methods *pMethods;
56626 		/* Open the target database */
56627 		rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zFilename,&pPager->pfd,pPager->iOpenFlags);
56628 		if( rc != UNQLITE_OK ){
56629 			unqliteGenErrorFormat(pPager->pDb,
56630 				"IO error while opening the target database file: %s",pPager->zFilename
56631 				);
56632 			return rc;
56633 		}
56634 		/* Try to obtain a shared lock */
56635 		rc = pager_wait_on_lock(pPager,SHARED_LOCK);
56636 		if( rc == UNQLITE_OK ){
56637 			if( pPager->iLock <= SHARED_LOCK ){
56638 				/* Rollback any hot journal */
56639 				rc = pager_journal_rollback(pPager,1);
56640 				if( rc != UNQLITE_OK ){
56641 					return rc;
56642 				}
56643 			}
56644 			/* Read the database header */
56645 			rc = pager_read_db_header(pPager);
56646 			if( rc != UNQLITE_OK ){
56647 				return rc;
56648 			}
56649 			if(pPager->dbSize > 0 ){
56650 				if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
56651 					const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
56652 					/* Obtain a read-only memory view of the whole file */
56653 					if( pVfs && pVfs->xMmap ){
56654 						int vr;
56655 						vr = pVfs->xMmap(pPager->zFilename,&pPager->pMmap,&pPager->dbByteSize);
56656 						if( vr != JX9_OK ){
56657 							/* Generate a warning */
56658 							unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
56659 							pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
56660 						}
56661 					}else{
56662 						/* Generate a warning */
56663 						unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
56664 						pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
56665 					}
56666 				}
56667 			}
56668 			/* Update the pager state */
56669 			pPager->iState = PAGER_READER;
56670 			/* Invoke the xOpen methods if available */
56671 			pMethods = pPager->pEngine->pIo->pMethods;
56672 			if( pMethods->xOpen ){
56673 				rc = pMethods->xOpen(pPager->pEngine,pPager->dbSize);
56674 				if( rc != UNQLITE_OK ){
56675 					unqliteGenErrorFormat(pPager->pDb,
56676 						"xOpen() method of the underlying KV engine '%z' failed",
56677 						&pPager->sKv
56678 						);
56679 					pager_unlock_db(pPager,NO_LOCK);
56680 					pPager->iState = PAGER_OPEN;
56681 					return rc;
56682 				}
56683 			}
56684 		}else if( rc == UNQLITE_BUSY ){
56685 			unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database");
56686 		}
56687 	}
56688 	return rc;
56689 }
56690 /*
56691 ** Begin a write-transaction on the specified pager object. If a
56692 ** write-transaction has already been opened, this function is a no-op.
56693 */
unqlitePagerBegin(Pager * pPager)56694 UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager)
56695 {
56696 	int rc;
56697 	/* Obtain a shared lock on the database first */
56698 	rc = pager_shared_lock(pPager);
56699 	if( rc != UNQLITE_OK ){
56700 		return rc;
56701 	}
56702 	if( pPager->iState >= PAGER_WRITER_LOCKED ){
56703 		return UNQLITE_OK;
56704 	}
56705 	if( pPager->is_rdonly ){
56706 		unqliteGenError(pPager->pDb,"Read-only database");
56707 		/* Read only database */
56708 		return UNQLITE_READ_ONLY;
56709 	}
56710 	/* Obtain a reserved lock on the database */
56711 	rc = pager_wait_on_lock(pPager,RESERVED_LOCK);
56712 	if( rc == UNQLITE_OK ){
56713 		/* Create the bitvec */
56714 		pPager->pVec = unqliteBitvecCreate(pPager->pAllocator,pPager->dbSize);
56715 		if( pPager->pVec == 0 ){
56716 			unqliteGenOutofMem(pPager->pDb);
56717 			rc = UNQLITE_NOMEM;
56718 			goto fail;
56719 		}
56720 		/* Change to the WRITER_LOCK state */
56721 		pPager->iState = PAGER_WRITER_LOCKED;
56722 		pPager->dbOrigSize = pPager->dbSize;
56723 		pPager->iJournalOfft = 0;
56724 		pPager->nRec = 0;
56725 		if( pPager->dbSize < 1 ){
56726 			/* Write the  database header */
56727 			rc = pager_create_header(pPager);
56728 			if( rc != UNQLITE_OK ){
56729 				goto fail;
56730 			}
56731 			pPager->dbSize = 1;
56732 		}
56733 	}else if( rc == UNQLITE_BUSY ){
56734 		unqliteGenError(pPager->pDb,"Another process or thread have a reserved lock on this database");
56735 	}
56736 	return rc;
56737 fail:
56738 	/* Downgrade to shared lock */
56739 	pager_unlock_db(pPager,SHARED_LOCK);
56740 	return rc;
56741 }
56742 /*
56743 ** This function is called at the start of every write transaction.
56744 ** There must already be a RESERVED or EXCLUSIVE lock on the database
56745 ** file when this routine is called.
56746 **
56747 */
unqliteOpenJournal(Pager * pPager)56748 static int unqliteOpenJournal(Pager *pPager)
56749 {
56750 	unsigned char *zHeader;
56751 	int rc = UNQLITE_OK;
56752 	if( pPager->is_mem || pPager->no_jrnl ){
56753 		/* Journaling is omitted for this database */
56754 		goto finish;
56755 	}
56756 	if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
56757 		/* Already opened */
56758 		return UNQLITE_OK;
56759 	}
56760 	/* Delete any previously journal with the same name */
56761 	unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
56762 	/* Open the journal file */
56763 	rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,
56764 		&pPager->pjfd,UNQLITE_OPEN_CREATE|UNQLITE_OPEN_READWRITE);
56765 	if( rc != UNQLITE_OK ){
56766 		unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: %s",pPager->zJournal);
56767 		return rc;
56768 	}
56769 	/* Write the journal header */
56770 	zHeader = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iSectorSize);
56771 	if( zHeader == 0 ){
56772 		rc = UNQLITE_NOMEM;
56773 		goto fail;
56774 	}
56775 	pager_write_journal_header(pPager,zHeader);
56776 	/* Perform the disk write */
56777 	rc = unqliteOsWrite(pPager->pjfd,zHeader,pPager->iSectorSize,0);
56778 	/* Offset to start writing from */
56779 	pPager->iJournalOfft = pPager->iSectorSize;
56780 	/* All done, journal will be synced later */
56781 	SyMemBackendFree(pPager->pAllocator,zHeader);
56782 finish:
56783 	if( rc == UNQLITE_OK ){
56784 		pPager->iState = PAGER_WRITER_CACHEMOD;
56785 		return UNQLITE_OK;
56786 	}
56787 fail:
56788 	/* Unlink the journal file if something goes wrong */
56789 	unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
56790 	unqliteOsDelete(pPager->pVfs,pPager->zJournal,0);
56791 	pPager->pjfd = 0;
56792 	return rc;
56793 }
56794 /*
56795 ** Sync the journal. In other words, make sure all the pages that have
56796 ** been written to the journal have actually reached the surface of the
56797 ** disk and can be restored in the event of a hot-journal rollback.
56798 *
56799 * This routine try also to obtain an exlusive lock on the database.
56800 */
unqliteFinalizeJournal(Pager * pPager,int * pRetry,int close_jrnl)56801 static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl)
56802 {
56803 	int rc;
56804 	*pRetry = 0;
56805 	/* Grab the exclusive lock first */
56806 	rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
56807 	if( rc != UNQLITE_OK ){
56808 		/* Retry the excusive lock process */
56809 		*pRetry = 1;
56810 		rc = UNQLITE_OK;
56811 	}
56812 	if( pPager->no_jrnl ){
56813 		/* Journaling is omitted, return immediately */
56814 		return UNQLITE_OK;
56815 	}
56816 	/* Write the total number of database records */
56817 	rc = WriteInt32(pPager->pjfd,pPager->nRec,8 /* sizeof(aJournalRec) */);
56818 	if( rc != UNQLITE_OK ){
56819 		if( pPager->nRec > 0 ){
56820 			return rc;
56821 		}else{
56822 			/* Not so fatal */
56823 			rc = UNQLITE_OK;
56824 		}
56825 	}
56826 	/* Sync the journal and close it */
56827 	rc = unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
56828 	if( close_jrnl ){
56829 		/* close the journal file */
56830 		if( UNQLITE_OK != unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd) ){
56831 			if( rc != UNQLITE_OK /* unqliteOsSync */ ){
56832 				return rc;
56833 			}
56834 		}
56835 		pPager->pjfd = 0;
56836 	}
56837 	if( (*pRetry) == 1 ){
56838 		if( pager_lock_db(pPager,EXCLUSIVE_LOCK) == UNQLITE_OK ){
56839 			/* Got exclusive lock */
56840 			*pRetry = 0;
56841 		}
56842 	}
56843 	return UNQLITE_OK;
56844 }
56845 /*
56846  * Mark a single data page as writeable. The page is written into the
56847  * main journal as required.
56848  */
page_write(Pager * pPager,Page * pPage)56849 static int page_write(Pager *pPager,Page *pPage)
56850 {
56851 	int rc;
56852 	if( !pPager->is_mem && !pPager->no_jrnl ){
56853 		/* Write the page to the transaction journal */
56854 		if( pPage->pgno < pPager->dbOrigSize && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
56855 			sxu32 cksum;
56856 			if( pPager->nRec == SXU32_HIGH ){
56857 				/* Journal Limit reached */
56858 				unqliteGenError(pPager->pDb,"Journal record limit reached, commit your changes");
56859 				return UNQLITE_LIMIT;
56860 			}
56861 			/* Write the page number */
56862 			rc = WriteInt64(pPager->pjfd,pPage->pgno,pPager->iJournalOfft);
56863 			if( rc != UNQLITE_OK ){ return rc; }
56864 			/* Write the raw page */
56865 			/** CODEC */
56866 			rc = unqliteOsWrite(pPager->pjfd,pPage->zData,pPager->iPageSize,pPager->iJournalOfft + 8);
56867 			if( rc != UNQLITE_OK ){ return rc; }
56868 			/* Compute the checksum */
56869 			cksum = pager_cksum(pPager,pPage->zData);
56870 			rc = WriteInt32(pPager->pjfd,cksum,pPager->iJournalOfft + 8 + pPager->iPageSize);
56871 			if( rc != UNQLITE_OK ){ return rc; }
56872 			/* Update the journal offset */
56873 			pPager->iJournalOfft += 8 /* page num */ + pPager->iPageSize + 4 /* cksum */;
56874 			pPager->nRec++;
56875 			/* Mark as journalled  */
56876 			unqliteBitvecSet(pPager->pVec,pPage->pgno);
56877 		}
56878 	}
56879 	/* Add the page to the dirty list */
56880 	pager_page_to_dirty_list(pPager,pPage);
56881 	/* Update the database size and return. */
56882 	if( (1 + pPage->pgno) > pPager->dbSize ){
56883 		pPager->dbSize = 1 + pPage->pgno;
56884 		if( pPager->dbSize == SXU64_HIGH ){
56885 			unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached");
56886 			return UNQLITE_LIMIT;
56887 		}
56888 	}
56889 	return UNQLITE_OK;
56890 }
56891 /*
56892 ** The argument is the first in a linked list of dirty pages connected
56893 ** by the PgHdr.pDirty pointer. This function writes each one of the
56894 ** in-memory pages in the list to the database file. The argument may
56895 ** be NULL, representing an empty list. In this case this function is
56896 ** a no-op.
56897 **
56898 ** The pager must hold at least a RESERVED lock when this function
56899 ** is called. Before writing anything to the database file, this lock
56900 ** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
56901 ** UNQLITE_BUSY is returned and no data is written to the database file.
56902 */
pager_write_dirty_pages(Pager * pPager,Page * pDirty)56903 static int pager_write_dirty_pages(Pager *pPager,Page *pDirty)
56904 {
56905 	int rc = UNQLITE_OK;
56906 	Page *pNext;
56907 	for(;;){
56908 		if( pDirty == 0 ){
56909 			break;
56910 		}
56911 		/* Point to the next dirty page */
56912 		pNext = pDirty->pDirtyPrev; /* Not a bug: Reverse link */
56913 		if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
56914 			rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
56915 			if( rc != UNQLITE_OK ){
56916 				/* A rollback should be done */
56917 				break;
56918 			}
56919 		}
56920 		/* Remove stale flags */
56921 		pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
56922 		if( pDirty->nRef < 1 ){
56923 			/* Unlink the page now it is unused */
56924 			pager_unlink_page(pPager,pDirty);
56925 			/* Release the page */
56926 			pager_release_page(pPager,pDirty);
56927 		}
56928 		/* Point to the next page */
56929 		pDirty = pNext;
56930 	}
56931 	pPager->pDirty = pPager->pFirstDirty = 0;
56932 	pPager->pHotDirty = pPager->pFirstHot = 0;
56933 	pPager->nHot = 0;
56934 	return rc;
56935 }
56936 /*
56937 ** The argument is the first in a linked list of hot dirty pages connected
56938 ** by the PgHdr.pHotDirty pointer. This function writes each one of the
56939 ** in-memory pages in the list to the database file. The argument may
56940 ** be NULL, representing an empty list. In this case this function is
56941 ** a no-op.
56942 **
56943 ** The pager must hold at least a RESERVED lock when this function
56944 ** is called. Before writing anything to the database file, this lock
56945 ** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
56946 ** UNQLITE_BUSY is returned and no data is written to the database file.
56947 */
pager_write_hot_dirty_pages(Pager * pPager,Page * pDirty)56948 static int pager_write_hot_dirty_pages(Pager *pPager,Page *pDirty)
56949 {
56950 	int rc = UNQLITE_OK;
56951 	Page *pNext;
56952 	for(;;){
56953 		if( pDirty == 0 ){
56954 			break;
56955 		}
56956 		/* Point to the next page */
56957 		pNext = pDirty->pPrevHot; /* Not a bug: Reverse link */
56958 		if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
56959 			rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
56960 			if( rc != UNQLITE_OK ){
56961 				break;
56962 			}
56963 		}
56964 		/* Remove stale flags */
56965 		pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
56966 		/* Unlink from the list of dirty pages */
56967 		if( pDirty->pDirtyPrev ){
56968 			pDirty->pDirtyPrev->pDirtyNext = pDirty->pDirtyNext;
56969 		}else{
56970 			pPager->pDirty = pDirty->pDirtyNext;
56971 		}
56972 		if( pDirty->pDirtyNext ){
56973 			pDirty->pDirtyNext->pDirtyPrev = pDirty->pDirtyPrev;
56974 		}else{
56975 			pPager->pFirstDirty = pDirty->pDirtyPrev;
56976 		}
56977 		/* Discard */
56978 		pager_unlink_page(pPager,pDirty);
56979 		/* Release the page */
56980 		pager_release_page(pPager,pDirty);
56981 		/* Next hot page */
56982 		pDirty = pNext;
56983 	}
56984 	return rc;
56985 }
56986 /*
56987  * Commit a transaction: Phase one.
56988  */
pager_commit_phase1(Pager * pPager)56989 static int pager_commit_phase1(Pager *pPager)
56990 {
56991 	int get_excl = 0;
56992 	Page *pDirty;
56993 	int rc;
56994 	/* If no database changes have been made, return early. */
56995 	if( pPager->iState < PAGER_WRITER_CACHEMOD ){
56996 		return UNQLITE_OK;
56997 	}
56998 	if( pPager->is_mem ){
56999 		/* An in-memory database */
57000 		return UNQLITE_OK;
57001 	}
57002 	if( pPager->is_rdonly ){
57003 		/* Read-Only DB */
57004 		unqliteGenError(pPager->pDb,"Read-Only database");
57005 		return UNQLITE_READ_ONLY;
57006 	}
57007 	/* Finalize the journal file */
57008 	rc = unqliteFinalizeJournal(pPager,&get_excl,1);
57009 	if( rc != UNQLITE_OK ){
57010 		return rc;
57011 	}
57012 	/* Get the dirty pages */
57013 	pDirty = pager_get_dirty_pages(pPager);
57014 	if( get_excl ){
57015 		/* Wait one last time for the exclusive lock */
57016 		rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
57017 		if( rc != UNQLITE_OK ){
57018 			unqliteGenError(pPager->pDb,"Cannot obtain an Exclusive lock on the target database");
57019 			return rc;
57020 		}
57021 	}
57022 	if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){
57023 		/* Synce the database first if a dirty commit have been applied */
57024 		unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL);
57025 	}
57026 	/* Write the dirty pages */
57027 	rc = pager_write_dirty_pages(pPager,pDirty);
57028 	if( rc != UNQLITE_OK ){
57029 		/* Rollback your DB */
57030 		pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
57031 		pPager->pFirstDirty = pDirty;
57032 		unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database");
57033 		return rc;
57034 	}
57035 	/* If the file on disk is not the same size as the database image,
57036      * then use unqliteOsTruncate to grow or shrink the file here.
57037      */
57038 	if( pPager->dbSize != pPager->dbOrigSize ){
57039 		unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
57040 	}
57041 	/* Sync the database file */
57042 	unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
57043 	/* Remove stale flags */
57044 	pPager->iJournalOfft = 0;
57045 	pPager->nRec = 0;
57046 	return UNQLITE_OK;
57047 }
57048 /*
57049  * Commit a transaction: Phase two.
57050  */
pager_commit_phase2(Pager * pPager)57051 static int pager_commit_phase2(Pager *pPager)
57052 {
57053 	if( !pPager->is_mem ){
57054 		if( pPager->iState == PAGER_OPEN ){
57055 			return UNQLITE_OK;
57056 		}
57057 		if( pPager->iState != PAGER_READER ){
57058 			if( !pPager->no_jrnl ){
57059 				/* Finally, unlink the journal file */
57060 				unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
57061 			}
57062 			/* Downgrade to shraed lock */
57063 			pager_unlock_db(pPager,SHARED_LOCK);
57064 			pPager->iState = PAGER_READER;
57065 			if( pPager->pVec ){
57066 				unqliteBitvecDestroy(pPager->pVec);
57067 				pPager->pVec = 0;
57068 			}
57069 		}
57070 	}
57071 	return UNQLITE_OK;
57072 }
57073 /*
57074  * Perform a dirty commit.
57075  */
pager_dirty_commit(Pager * pPager)57076 static int pager_dirty_commit(Pager *pPager)
57077 {
57078 	int get_excl = 0;
57079 	Page *pHot;
57080 	int rc;
57081 	/* Finalize the journal file without closing it */
57082 	rc = unqliteFinalizeJournal(pPager,&get_excl,0);
57083 	if( rc != UNQLITE_OK ){
57084 		/* It's not a fatal error if something goes wrong here since
57085 		 * its not the final commit.
57086 		 */
57087 		return UNQLITE_OK;
57088 	}
57089 	/* Point to the list of hot pages */
57090 	pHot = pager_get_hot_pages(pPager);
57091 	if( pHot == 0 ){
57092 		return UNQLITE_OK;
57093 	}
57094 	if( get_excl ){
57095 		/* Wait one last time for the exclusive lock */
57096 		rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
57097 		if( rc != UNQLITE_OK ){
57098 			/* Not so fatal, will try another time */
57099 			return UNQLITE_OK;
57100 		}
57101 	}
57102 	/* Tell that a dirty commit happen */
57103 	pPager->iFlags |= PAGER_CTRL_DIRTY_COMMIT;
57104 	/* Write the hot pages now */
57105 	rc = pager_write_hot_dirty_pages(pPager,pHot);
57106 	if( rc != UNQLITE_OK ){
57107 		pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
57108 		unqliteGenError(pPager->pDb,"IO error while writing hot dirty pages, rollback your database");
57109 		return rc;
57110 	}
57111 	pPager->pFirstHot = pPager->pHotDirty = 0;
57112 	pPager->nHot = 0;
57113 	/* No need to sync the database file here, since the journal is already
57114 	 * open here and this is not the final commit.
57115 	 */
57116 	return UNQLITE_OK;
57117 }
57118 /*
57119 ** Commit a transaction and sync the database file for the pager pPager.
57120 **
57121 ** This routine ensures that:
57122 **
57123 **   * the journal is synced,
57124 **   * all dirty pages are written to the database file,
57125 **   * the database file is truncated (if required), and
57126 **   * the database file synced.
57127 **   * the journal file is deleted.
57128 */
unqlitePagerCommit(Pager * pPager)57129 UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager)
57130 {
57131 	int rc;
57132 	/* Commit: Phase One */
57133 	rc = pager_commit_phase1(pPager);
57134 	if( rc != UNQLITE_OK ){
57135 		goto fail;
57136 	}
57137 	/* Commit: Phase Two */
57138 	rc = pager_commit_phase2(pPager);
57139 	if( rc != UNQLITE_OK ){
57140 		goto fail;
57141 	}
57142 	/* Remove stale flags */
57143 	pPager->iFlags &= ~PAGER_CTRL_COMMIT_ERR;
57144 	/* All done */
57145 	return UNQLITE_OK;
57146 fail:
57147 	/* Disable the auto-commit flag */
57148 	pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
57149 	return rc;
57150 }
57151 /*
57152  * Reset the pager to its initial state. This is caused by
57153  * a rollback operation.
57154  */
pager_reset_state(Pager * pPager,int bResetKvEngine)57155 static int pager_reset_state(Pager *pPager,int bResetKvEngine)
57156 {
57157 	unqlite_kv_engine *pEngine = pPager->pEngine;
57158 	Page *pNext,*pPtr = pPager->pAll;
57159 	const unqlite_kv_io *pIo;
57160 	int rc;
57161 	/* Remove stale flags */
57162 	pPager->iFlags &= ~(PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT);
57163 	pPager->iJournalOfft = 0;
57164 	pPager->nRec = 0;
57165 	/* Database original size */
57166 	pPager->dbSize = pPager->dbOrigSize;
57167 	/* Discard all in-memory pages */
57168 	for(;;){
57169 		if( pPtr == 0 ){
57170 			break;
57171 		}
57172 		pNext = pPtr->pNext; /* Reverse link */
57173 		/* Remove stale flags */
57174 		pPtr->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
57175 		/* Release the page */
57176 		pager_release_page(pPager,pPtr);
57177 		/* Point to the next page */
57178 		pPtr = pNext;
57179 	}
57180 	pPager->pAll = 0;
57181 	pPager->nPage = 0;
57182 	pPager->pDirty = pPager->pFirstDirty = 0;
57183 	pPager->pHotDirty = pPager->pFirstHot = 0;
57184 	pPager->nHot = 0;
57185 	if( pPager->apHash ){
57186 		/* Zero the table */
57187 		SyZero((void *)pPager->apHash,sizeof(Page *) * pPager->nSize);
57188 	}
57189 	if( pPager->pVec ){
57190 		unqliteBitvecDestroy(pPager->pVec);
57191 		pPager->pVec = 0;
57192 	}
57193 	/* Switch back to shared lock */
57194 	pager_unlock_db(pPager,SHARED_LOCK);
57195 	pPager->iState = PAGER_READER;
57196 	if( bResetKvEngine ){
57197 		/* Reset the underlying KV engine */
57198 		pIo = pEngine->pIo;
57199 		if( pIo->pMethods->xRelease ){
57200 			/* Call the release callback */
57201 			pIo->pMethods->xRelease(pEngine);
57202 		}
57203 		/* Zero the structure */
57204 		SyZero(pEngine,(sxu32)pIo->pMethods->szKv);
57205 		/* Fill in */
57206 		pEngine->pIo = pIo;
57207 		if( pIo->pMethods->xInit ){
57208 			/* Call the init method */
57209 			rc = pIo->pMethods->xInit(pEngine,pPager->iPageSize);
57210 			if( rc != UNQLITE_OK ){
57211 				return rc;
57212 			}
57213 		}
57214 		if( pIo->pMethods->xOpen ){
57215 			/* Call the xOpen method */
57216 			rc = pIo->pMethods->xOpen(pEngine,pPager->dbSize);
57217 			if( rc != UNQLITE_OK ){
57218 				return rc;
57219 			}
57220 		}
57221 	}
57222 	/* All done */
57223 	return UNQLITE_OK;
57224 }
57225 /*
57226 ** If a write transaction is open, then all changes made within the
57227 ** transaction are reverted and the current write-transaction is closed.
57228 ** The pager falls back to PAGER_READER state if successful.
57229 **
57230 ** Otherwise, in rollback mode, this function performs two functions:
57231 **
57232 **   1) It rolls back the journal file, restoring all database file and
57233 **      in-memory cache pages to the state they were in when the transaction
57234 **      was opened, and
57235 **
57236 **   2) It finalizes the journal file, so that it is not used for hot
57237 **      rollback at any point in the future (i.e. deletion).
57238 **
57239 ** Finalization of the journal file (task 2) is only performed if the
57240 ** rollback is successful.
57241 **
57242 */
unqlitePagerRollback(Pager * pPager,int bResetKvEngine)57243 UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine)
57244 {
57245 	int rc = UNQLITE_OK;
57246 	if( pPager->iState < PAGER_WRITER_LOCKED ){
57247 		/* A write transaction must be opened */
57248 		return UNQLITE_OK;
57249 	}
57250 	if( pPager->is_mem ){
57251 		/* As of this release 1.1.6: Transactions are not supported for in-memory databases */
57252 		return UNQLITE_OK;
57253 	}
57254 	if( pPager->is_rdonly ){
57255 		/* Read-Only DB */
57256 		unqliteGenError(pPager->pDb,"Read-Only database");
57257 		return UNQLITE_READ_ONLY;
57258 	}
57259 	if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
57260 		if( !pPager->no_jrnl ){
57261 			/* Close any outstanding joural file */
57262 			if( pPager->pjfd ){
57263 				/* Sync the journal file */
57264 				unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
57265 			}
57266 			unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
57267 			pPager->pjfd = 0;
57268 			if( pPager->iFlags & (PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT) ){
57269 				/* Perform the rollback */
57270 				rc = pager_journal_rollback(pPager,0);
57271 				if( rc != UNQLITE_OK ){
57272 					/* Set the auto-commit flag */
57273 					pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
57274 					return rc;
57275 				}
57276 			}
57277 		}
57278 		/* Unlink the journal file */
57279 		unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
57280 		/* Reset the pager state */
57281 		rc = pager_reset_state(pPager,bResetKvEngine);
57282 		if( rc != UNQLITE_OK ){
57283 			/* Mostly an unlikely scenario */
57284 			pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; /* Set the auto-commit flag */
57285 			unqliteGenError(pPager->pDb,"Error while reseting pager to its initial state");
57286 			return rc;
57287 		}
57288 	}else{
57289 		/* Downgrade to shared lock */
57290 		pager_unlock_db(pPager,SHARED_LOCK);
57291 		pPager->iState = PAGER_READER;
57292 	}
57293 	return UNQLITE_OK;
57294 }
57295 /*
57296  *  Mark a data page as non writeable.
57297  */
unqlitePagerDontWrite(unqlite_page * pMyPage)57298 static int unqlitePagerDontWrite(unqlite_page *pMyPage)
57299 {
57300 	Page *pPage = (Page *)pMyPage;
57301 	if( pPage->pgno > 0 /* Page 0 is always writeable */ ){
57302 		pPage->flags |= PAGE_DONT_WRITE;
57303 	}
57304 	return UNQLITE_OK;
57305 }
57306 /*
57307 ** Mark a data page as writeable. This routine must be called before
57308 ** making changes to a page. The caller must check the return value
57309 ** of this function and be careful not to change any page data unless
57310 ** this routine returns UNQLITE_OK.
57311 */
unqlitePageWrite(unqlite_page * pMyPage)57312 static int unqlitePageWrite(unqlite_page *pMyPage)
57313 {
57314 	Page *pPage = (Page *)pMyPage;
57315 	Pager *pPager = pPage->pPager;
57316 	int rc;
57317 	/* Begin the write transaction */
57318 	rc = unqlitePagerBegin(pPager);
57319 	if( rc != UNQLITE_OK ){
57320 		return rc;
57321 	}
57322 	if( pPager->iState == PAGER_WRITER_LOCKED ){
57323 		/* The journal file needs to be opened. Higher level routines have already
57324 		 ** obtained the necessary locks to begin the write-transaction, but the
57325 		 ** rollback journal might not yet be open. Open it now if this is the case.
57326 		 */
57327 		rc = unqliteOpenJournal(pPager);
57328 		if( rc != UNQLITE_OK ){
57329 			return rc;
57330 		}
57331 	}
57332 	if( pPager->nHot > 127 ){
57333 		/* Write hot dirty pages */
57334 		rc = pager_dirty_commit(pPager);
57335 		if( rc != UNQLITE_OK ){
57336 			/* A rollback must be done */
57337 			unqliteGenError(pPager->pDb,"Please perform a rollback");
57338 			return rc;
57339 		}
57340 	}
57341 	/* Write the page to the journal file */
57342 	rc = page_write(pPager,pPage);
57343 	return rc;
57344 }
57345 /*
57346 ** Acquire a reference to page number pgno in pager pPager (a page
57347 ** reference has type unqlite_page*). If the requested reference is
57348 ** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned.
57349 **
57350 ** If the requested page is already in the cache, it is returned.
57351 ** Otherwise, a new page object is allocated and populated with data
57352 ** read from the database file.
57353 */
unqlitePagerAcquire(Pager * pPager,pgno pgno,unqlite_page ** ppPage,int fetchOnly,int noContent)57354 static int unqlitePagerAcquire(
57355   Pager *pPager,      /* The pager open on the database file */
57356   pgno pgno,          /* Page number to fetch */
57357   unqlite_page **ppPage,    /* OUT: Acquired page */
57358   int fetchOnly,      /* Cache lookup only */
57359   int noContent       /* Do not bother reading content from disk if true */
57360 )
57361 {
57362 	Page *pPage;
57363 	int rc;
57364 	/* Acquire a shared lock (if not yet done) on the database and rollback any hot-journal if present */
57365 	rc = pager_shared_lock(pPager);
57366 	if( rc != UNQLITE_OK ){
57367 		return rc;
57368 	}
57369 	/* Fetch the page from the cache */
57370 	pPage = pager_fetch_page(pPager,pgno);
57371 	if( fetchOnly ){
57372 		if( ppPage ){
57373 			*ppPage = (unqlite_page *)pPage;
57374 		}
57375 		return pPage ? UNQLITE_OK : UNQLITE_NOTFOUND;
57376 	}
57377 	if( pPage == 0 ){
57378 		/* Allocate a new page */
57379 		pPage = pager_alloc_page(pPager,pgno);
57380 		if( pPage == 0 ){
57381 			unqliteGenOutofMem(pPager->pDb);
57382 			return UNQLITE_NOMEM;
57383 		}
57384 		/* Read page contents */
57385 		rc = pager_get_page_contents(pPager,pPage,noContent);
57386 		if( rc != UNQLITE_OK ){
57387 			SyMemBackendPoolFree(pPager->pAllocator,pPage);
57388 			return rc;
57389 		}
57390 		/* Link the page */
57391 		pager_link_page(pPager,pPage);
57392 	}else{
57393 		if( ppPage ){
57394 			page_ref(pPage);
57395 		}
57396 	}
57397 	/* All done, page is loaded in memeory */
57398 	if( ppPage ){
57399 		*ppPage = (unqlite_page *)pPage;
57400 	}
57401 	return UNQLITE_OK;
57402 }
57403 /*
57404  * Return true if we are dealing with an in-memory database.
57405  */
unqliteInMemory(const char * zFilename)57406 static int unqliteInMemory(const char *zFilename)
57407 {
57408 	sxu32 n;
57409 	if( SX_EMPTY_STR(zFilename) ){
57410 		/* NULL or the empty string means an in-memory database */
57411 		return TRUE;
57412 	}
57413 	n = SyStrlen(zFilename);
57414 	if( n == sizeof(":mem:") - 1 &&
57415 		SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){
57416 			return TRUE;
57417 	}
57418 	if( n == sizeof(":memory:") - 1 &&
57419 		SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){
57420 			return TRUE;
57421 	}
57422 	return FALSE;
57423 }
57424 /*
57425  * Allocate a new KV cursor.
57426  */
unqliteInitCursor(unqlite * pDb,unqlite_kv_cursor ** ppOut)57427 UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut)
57428 {
57429 	unqlite_kv_methods *pMethods;
57430 	unqlite_kv_cursor *pCur;
57431 	sxu32 nByte;
57432 	/* Storage engine methods */
57433 	pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
57434 	if( pMethods->szCursor < 1 ){
57435 		/* Implementation does not supprt cursors */
57436 		unqliteGenErrorFormat(pDb,"Storage engine '%s' does not support cursors",pMethods->zName);
57437 		return UNQLITE_NOTIMPLEMENTED;
57438 	}
57439 	nByte = pMethods->szCursor;
57440 	if( nByte < sizeof(unqlite_kv_cursor) ){
57441 		nByte += sizeof(unqlite_kv_cursor);
57442 	}
57443 	pCur = (unqlite_kv_cursor *)SyMemBackendPoolAlloc(&pDb->sMem,nByte);
57444 	if( pCur == 0 ){
57445 		unqliteGenOutofMem(pDb);
57446 		return UNQLITE_NOMEM;
57447 	}
57448 	/* Zero the structure */
57449 	SyZero(pCur,nByte);
57450 	/* Save the cursor */
57451 	pCur->pStore = pDb->sDB.pPager->pEngine;
57452 	/* Invoke the initialization callback if any */
57453 	if( pMethods->xCursorInit ){
57454 		pMethods->xCursorInit(pCur);
57455 	}
57456 	/* All done */
57457 	*ppOut = pCur;
57458 	return UNQLITE_OK;
57459 }
57460 /*
57461  * Release a cursor.
57462  */
unqliteReleaseCursor(unqlite * pDb,unqlite_kv_cursor * pCur)57463 UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur)
57464 {
57465 	unqlite_kv_methods *pMethods;
57466 	/* Storage engine methods */
57467 	pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
57468 	/* Invoke the release callback if available */
57469 	if( pMethods->xCursorRelease ){
57470 		pMethods->xCursorRelease(pCur);
57471 	}
57472 	/* Finally, free the whole instance */
57473 	SyMemBackendPoolFree(&pDb->sMem,pCur);
57474 	return UNQLITE_OK;
57475 }
57476 /*
57477  * Release the underlying KV storage engine and invoke
57478  * its associated callbacks if available.
57479  */
pager_release_kv_engine(Pager * pPager)57480 static void pager_release_kv_engine(Pager *pPager)
57481 {
57482 	unqlite_kv_engine *pEngine = pPager->pEngine;
57483 	unqlite_db *pStorage = &pPager->pDb->sDB;
57484 	if( pStorage->pCursor ){
57485 		/* Release the associated cursor */
57486 		unqliteReleaseCursor(pPager->pDb,pStorage->pCursor);
57487 		pStorage->pCursor = 0;
57488 	}
57489 	if( pEngine->pIo->pMethods->xRelease ){
57490 		pEngine->pIo->pMethods->xRelease(pEngine);
57491 	}
57492 	/* Release the whole instance */
57493 	SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine->pIo);
57494 	SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine);
57495 	pPager->pEngine = 0;
57496 }
57497 /* Forward declaration */
57498 static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo);
57499 /*
57500  * Allocate, initialize and register a new KV storage engine
57501  * within this database instance.
57502  */
unqlitePagerRegisterKvEngine(Pager * pPager,unqlite_kv_methods * pMethods)57503 UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods)
57504 {
57505 	unqlite_db *pStorage = &pPager->pDb->sDB;
57506 	unqlite *pDb = pPager->pDb;
57507 	unqlite_kv_engine *pEngine;
57508 	unqlite_kv_io *pIo;
57509 	sxu32 nByte;
57510 	int rc;
57511 	if( pPager->pEngine ){
57512 		if( pMethods == pPager->pEngine->pIo->pMethods ){
57513 			/* Ticket 1432: Same implementation */
57514 			return UNQLITE_OK;
57515 		}
57516 		/* Release the old KV engine */
57517 		pager_release_kv_engine(pPager);
57518 	}
57519 	/* Allocate a new KV engine instance */
57520 	nByte = (sxu32)pMethods->szKv;
57521 	pEngine = (unqlite_kv_engine *)SyMemBackendAlloc(&pDb->sMem,nByte);
57522 	if( pEngine == 0 ){
57523 		unqliteGenOutofMem(pDb);
57524 		return UNQLITE_NOMEM;
57525 	}
57526 	pIo = (unqlite_kv_io *)SyMemBackendAlloc(&pDb->sMem,sizeof(unqlite_kv_io));
57527 	if( pIo == 0 ){
57528 		SyMemBackendFree(&pDb->sMem,pEngine);
57529 		unqliteGenOutofMem(pDb);
57530 		return UNQLITE_NOMEM;
57531 	}
57532 	/* Zero the structure */
57533 	SyZero(pIo,sizeof(unqlite_io_methods));
57534 	SyZero(pEngine,nByte);
57535 	/* Populate the IO structure */
57536 	pager_kv_io_init(pPager,pMethods,pIo);
57537 	pEngine->pIo = pIo;
57538 	/* Invoke the init callback if avaialble */
57539 	if( pMethods->xInit ){
57540 		rc = pMethods->xInit(pEngine,unqliteGetPageSize());
57541 		if( rc != UNQLITE_OK ){
57542 			unqliteGenErrorFormat(pDb,
57543 				"xInit() method of the underlying KV engine '%z' failed",&pPager->sKv);
57544 			goto fail;
57545 		}
57546 		pEngine->pIo = pIo;
57547 	}
57548 	pPager->pEngine = pEngine;
57549 	/* Allocate a new cursor */
57550 	rc = unqliteInitCursor(pDb,&pStorage->pCursor);
57551 	if( rc != UNQLITE_OK ){
57552 		goto fail;
57553 	}
57554 	return UNQLITE_OK;
57555 fail:
57556 	SyMemBackendFree(&pDb->sMem,pEngine);
57557 	SyMemBackendFree(&pDb->sMem,pIo);
57558 	return rc;
57559 }
57560 /*
57561  * Return the underlying KV storage engine instance.
57562  */
unqlitePagerGetKvEngine(unqlite * pDb)57563 UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb)
57564 {
57565 	return pDb->sDB.pPager->pEngine;
57566 }
57567 /*
57568 * Allocate and initialize a new Pager object. The pager should
57569 * eventually be freed by passing it to unqlitePagerClose().
57570 *
57571 * The zFilename argument is the path to the database file to open.
57572 * If zFilename is NULL or ":memory:" then all information is held
57573 * in cache. It is never written to disk.  This can be used to implement
57574 * an in-memory database.
57575 */
unqlitePagerOpen(unqlite_vfs * pVfs,unqlite * pDb,const char * zFilename,unsigned int iFlags)57576 UNQLITE_PRIVATE int unqlitePagerOpen(
57577   unqlite_vfs *pVfs,       /* The virtual file system to use */
57578   unqlite *pDb,            /* Database handle */
57579   const char *zFilename,   /* Name of the database file to open */
57580   unsigned int iFlags      /* flags controlling this file */
57581   )
57582 {
57583 	unqlite_kv_methods *pMethods = 0;
57584 	int is_mem,rd_only,no_jrnl;
57585 	Pager *pPager;
57586 	sxu32 nByte;
57587 	sxu32 nLen;
57588 	int rc;
57589 
57590 	/* Select the appropriate KV storage subsytem  */
57591 	if( (iFlags & UNQLITE_OPEN_IN_MEMORY) || unqliteInMemory(zFilename) ){
57592 		/* An in-memory database, record that  */
57593 		pMethods = unqliteFindKVStore("mem",sizeof("mem") - 1); /* Always available */
57594 		iFlags |= UNQLITE_OPEN_IN_MEMORY;
57595 	}else{
57596 		/* Install the default key value storage subsystem [i.e. Linear Hash] */
57597 		pMethods = unqliteFindKVStore("hash",sizeof("hash")-1);
57598 		if( pMethods == 0 ){
57599 			/* Use the b+tree storage backend if the linear hash storage is not available */
57600 			pMethods = unqliteFindKVStore("btree",sizeof("btree")-1);
57601 		}
57602 	}
57603 	if( pMethods == 0 ){
57604 		/* Can't happen */
57605 		unqliteGenError(pDb,"Cannot install a default Key/Value storage engine");
57606 		return UNQLITE_NOTIMPLEMENTED;
57607 	}
57608 	is_mem = (iFlags & UNQLITE_OPEN_IN_MEMORY) != 0;
57609 	rd_only = (iFlags & UNQLITE_OPEN_READONLY) != 0;
57610 	no_jrnl = (iFlags & UNQLITE_OPEN_OMIT_JOURNALING) != 0;
57611 	rc = UNQLITE_OK;
57612 	if( is_mem ){
57613 		/* Omit journaling for in-memory database */
57614 		no_jrnl = 1;
57615 	}
57616 	/* Total number of bytes to allocate */
57617 	nByte = sizeof(Pager);
57618 	nLen = 0;
57619 	if( !is_mem ){
57620 		nLen = SyStrlen(zFilename);
57621 		nByte += pVfs->mxPathname + nLen + sizeof(char) /* null termniator */;
57622 	}
57623 	/* Allocate */
57624 	pPager = (Pager *)SyMemBackendAlloc(&pDb->sMem,nByte);
57625 	if( pPager == 0 ){
57626 		return UNQLITE_NOMEM;
57627 	}
57628 	/* Zero the structure */
57629 	SyZero(pPager,nByte);
57630 	/* Fill-in the structure */
57631 	pPager->pAllocator = &pDb->sMem;
57632 	pPager->pDb = pDb;
57633 	pDb->sDB.pPager = pPager;
57634 	/* Allocate page table */
57635 	pPager->nSize = 128; /* Must be a power of two */
57636 	nByte = pPager->nSize * sizeof(Page *);
57637 	pPager->apHash = (Page **)SyMemBackendAlloc(pPager->pAllocator,nByte);
57638 	if( pPager->apHash == 0 ){
57639 		rc = UNQLITE_NOMEM;
57640 		goto fail;
57641 	}
57642 	SyZero(pPager->apHash,nByte);
57643 	pPager->is_mem = is_mem;
57644 	pPager->no_jrnl = no_jrnl;
57645 	pPager->is_rdonly = rd_only;
57646 	pPager->iOpenFlags = iFlags;
57647 	pPager->pVfs = pVfs;
57648 	SyRandomnessInit(&pPager->sPrng,0,0);
57649 	SyRandomness(&pPager->sPrng,(void *)&pPager->cksumInit,sizeof(sxu32));
57650 	/* Unlimited cache size */
57651 	pPager->nCacheMax = SXU32_HIGH;
57652 	/* Copy filename and journal name */
57653 	if( !is_mem ){
57654 		pPager->zFilename = (char *)&pPager[1];
57655 		rc = UNQLITE_OK;
57656 		if( pVfs->xFullPathname ){
57657 			rc = pVfs->xFullPathname(pVfs,zFilename,pVfs->mxPathname + nLen,pPager->zFilename);
57658 		}
57659 		if( rc != UNQLITE_OK ){
57660 			/* Simple filename copy */
57661 			SyMemcpy(zFilename,pPager->zFilename,nLen);
57662 			pPager->zFilename[nLen] = 0;
57663 			rc = UNQLITE_OK;
57664 		}else{
57665 			nLen = SyStrlen(pPager->zFilename);
57666 		}
57667 		pPager->zJournal = (char *) SyMemBackendAlloc(pPager->pAllocator,nLen + sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) + sizeof(char));
57668 		if( pPager->zJournal == 0 ){
57669 			rc = UNQLITE_NOMEM;
57670 			goto fail;
57671 		}
57672 		/* Copy filename */
57673 		SyMemcpy(pPager->zFilename,pPager->zJournal,nLen);
57674 		/* Copy journal suffix */
57675 		SyMemcpy(UNQLITE_JOURNAL_FILE_SUFFIX,&pPager->zJournal[nLen],sizeof(UNQLITE_JOURNAL_FILE_SUFFIX)-1);
57676 		/* Append the nul terminator to the journal path */
57677 		pPager->zJournal[nLen + ( sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) - 1)] = 0;
57678 	}
57679 	/* Finally, register the selected KV engine */
57680 	rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
57681 	if( rc != UNQLITE_OK ){
57682 		goto fail;
57683 	}
57684 	/* Set the pager state */
57685 	if( pPager->is_mem ){
57686 		pPager->iState = PAGER_WRITER_FINISHED;
57687 		pPager->iLock = EXCLUSIVE_LOCK;
57688 	}else{
57689 		pPager->iState = PAGER_OPEN;
57690 		pPager->iLock = NO_LOCK;
57691 	}
57692 	/* All done, ready for processing */
57693 	return UNQLITE_OK;
57694 fail:
57695 	SyMemBackendFree(&pDb->sMem,pPager);
57696 	return rc;
57697 }
57698 /*
57699  * Set a cache limit. Note that, this is a simple hint, the pager is not
57700  * forced to honor this limit.
57701  */
unqlitePagerSetCachesize(Pager * pPager,int mxPage)57702 UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage)
57703 {
57704 	if( mxPage < 256 ){
57705 		return UNQLITE_INVALID;
57706 	}
57707 	pPager->nCacheMax = mxPage;
57708 	return UNQLITE_OK;
57709 }
57710 /*
57711  * Shutdown the page cache. Free all memory and close the database file.
57712  */
unqlitePagerClose(Pager * pPager)57713 UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager)
57714 {
57715 	/* Release the KV engine */
57716 	pager_release_kv_engine(pPager);
57717 	if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
57718 		const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
57719 		if( pVfs && pVfs->xUnmap && pPager->pMmap ){
57720 			pVfs->xUnmap(pPager->pMmap,pPager->dbByteSize);
57721 		}
57722 	}
57723 	if( !pPager->is_mem && pPager->iState > PAGER_OPEN ){
57724 		/* Release all lock on this database handle */
57725 		pager_unlock_db(pPager,NO_LOCK);
57726 		/* Close the file  */
57727 		unqliteOsCloseFree(pPager->pAllocator,pPager->pfd);
57728 	}
57729 	if( pPager->pVec ){
57730 		unqliteBitvecDestroy(pPager->pVec);
57731 		pPager->pVec = 0;
57732 	}
57733 	return UNQLITE_OK;
57734 }
57735 /*
57736  * Generate a random string.
57737  */
unqlitePagerRandomString(Pager * pPager,char * zBuf,sxu32 nLen)57738 UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen)
57739 {
57740 	static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
57741 	sxu32 i;
57742 	/* Generate a binary string first */
57743 	SyRandomness(&pPager->sPrng,zBuf,nLen);
57744 	/* Turn the binary string into english based alphabet */
57745 	for( i = 0 ; i < nLen ; ++i ){
57746 		 zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
57747 	 }
57748 }
57749 /*
57750  * Generate a random number.
57751  */
unqlitePagerRandomNum(Pager * pPager)57752 UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager)
57753 {
57754 	sxu32 iNum;
57755 	SyRandomness(&pPager->sPrng,(void *)&iNum,sizeof(iNum));
57756 	return iNum;
57757 }
57758 /* Exported KV IO Methods */
57759 /*
57760  * Refer to [unqlitePagerAcquire()]
57761  */
unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page ** ppPage)57762 static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
57763 {
57764 	int rc;
57765 	rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0);
57766 	return rc;
57767 }
57768 /*
57769  * Refer to [unqlitePagerAcquire()]
57770  */
unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page ** ppPage)57771 static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
57772 {
57773 	int rc;
57774 	rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0);
57775 	return rc;
57776 }
57777 /*
57778  * Refer to [unqlitePagerAcquire()]
57779  */
unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page ** ppPage)57780 static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage)
57781 {
57782 	Pager *pPager = (Pager *)pHandle;
57783 	int rc;
57784 	/*
57785 	 * Acquire a reader-lock first so that pPager->dbSize get initialized.
57786 	 */
57787 	rc = pager_shared_lock(pPager);
57788 	if( rc == UNQLITE_OK ){
57789 		rc = unqlitePagerAcquire(pPager,pPager->dbSize == 0 ? /* Page 0 is reserved */ 1 : pPager->dbSize ,ppPage,0,0);
57790 	}
57791 	return rc;
57792 }
57793 /*
57794  * Refer to [unqlitePageWrite()]
57795  */
unqliteKvIopageWrite(unqlite_page * pPage)57796 static int unqliteKvIopageWrite(unqlite_page *pPage)
57797 {
57798 	int rc;
57799 	if( pPage == 0 ){
57800 		/* TICKET 1433-0348 */
57801 		return UNQLITE_OK;
57802 	}
57803 	rc = unqlitePageWrite(pPage);
57804 	return rc;
57805 }
57806 /*
57807  * Refer to [unqlitePagerDontWrite()]
57808  */
unqliteKvIoPageDontWrite(unqlite_page * pPage)57809 static int unqliteKvIoPageDontWrite(unqlite_page *pPage)
57810 {
57811 	int rc;
57812 	if( pPage == 0 ){
57813 		/* TICKET 1433-0348 */
57814 		return UNQLITE_OK;
57815 	}
57816 	rc = unqlitePagerDontWrite(pPage);
57817 	return rc;
57818 }
57819 /*
57820  * Refer to [unqliteBitvecSet()]
57821  */
unqliteKvIoPageDontJournal(unqlite_page * pRaw)57822 static int unqliteKvIoPageDontJournal(unqlite_page *pRaw)
57823 {
57824 	Page *pPage = (Page *)pRaw;
57825 	Pager *pPager;
57826 	if( pPage == 0 ){
57827 		/* TICKET 1433-0348 */
57828 		return UNQLITE_OK;
57829 	}
57830 	pPager = pPage->pPager;
57831 	if( pPager->iState >= PAGER_WRITER_LOCKED ){
57832 		if( !pPager->no_jrnl && pPager->pVec && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
57833 			unqliteBitvecSet(pPager->pVec,pPage->pgno);
57834 		}
57835 	}
57836 	return UNQLITE_OK;
57837 }
57838 /*
57839  * Do not add a page to the hot dirty list.
57840  */
unqliteKvIoPageDontMakeHot(unqlite_page * pRaw)57841 static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw)
57842 {
57843 	Page *pPage = (Page *)pRaw;
57844 
57845 	if( pPage == 0 ){
57846 		/* TICKET 1433-0348 */
57847 		return UNQLITE_OK;
57848 	}
57849 	pPage->flags |= PAGE_DONT_MAKE_HOT;
57850 
57851 	/* Remove from hot dirty list if it is already there */
57852 	if( pPage->flags & PAGE_HOT_DIRTY ){
57853 		Pager *pPager = pPage->pPager;
57854 		if( pPage->pNextHot ){
57855 			pPage->pNextHot->pPrevHot = pPage->pPrevHot;
57856 		}
57857 		if( pPage->pPrevHot ){
57858 			pPage->pPrevHot->pNextHot = pPage->pNextHot;
57859 		}
57860 		if( pPager->pFirstHot == pPage ){
57861 			pPager->pFirstHot = pPage->pPrevHot;
57862 		}
57863 		if( pPager->pHotDirty == pPage ){
57864 			pPager->pHotDirty = pPage->pNextHot;
57865 		}
57866 		pPager->nHot--;
57867 		pPage->flags &= ~PAGE_HOT_DIRTY;
57868 	}
57869 
57870 	return UNQLITE_OK;
57871 }
57872 /*
57873  * Refer to [page_ref()]
57874  */
unqliteKvIopage_ref(unqlite_page * pPage)57875 static int unqliteKvIopage_ref(unqlite_page *pPage)
57876 {
57877 	if( pPage ){
57878 		page_ref((Page *)pPage);
57879 	}
57880 	return UNQLITE_OK;
57881 }
57882 /*
57883  * Refer to [page_unref()]
57884  */
unqliteKvIoPageUnRef(unqlite_page * pPage)57885 static int unqliteKvIoPageUnRef(unqlite_page *pPage)
57886 {
57887 	if( pPage ){
57888 		page_unref((Page *)pPage);
57889 	}
57890 	return UNQLITE_OK;
57891 }
57892 /*
57893  * Refer to the declaration of the [Pager] structure
57894  */
unqliteKvIoReadOnly(unqlite_kv_handle pHandle)57895 static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle)
57896 {
57897 	return ((Pager *)pHandle)->is_rdonly;
57898 }
57899 /*
57900  * Refer to the declaration of the [Pager] structure
57901  */
unqliteKvIoPageSize(unqlite_kv_handle pHandle)57902 static int unqliteKvIoPageSize(unqlite_kv_handle pHandle)
57903 {
57904 	return ((Pager *)pHandle)->iPageSize;
57905 }
57906 /*
57907  * Refer to the declaration of the [Pager] structure
57908  */
unqliteKvIoTempPage(unqlite_kv_handle pHandle)57909 static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle)
57910 {
57911 	return ((Pager *)pHandle)->zTmpPage;
57912 }
57913 /*
57914  * Set a page unpin callback.
57915  * Refer to the declaration of the [Pager] structure
57916  */
unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (* xPageUnpin)(void *))57917 static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(void *))
57918 {
57919 	Pager *pPager = (Pager *)pHandle;
57920 	pPager->xPageUnpin = xPageUnpin;
57921 }
57922 /*
57923  * Set a page reload callback.
57924  * Refer to the declaration of the [Pager] structure
57925  */
unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (* xPageReload)(void *))57926 static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)(void *))
57927 {
57928 	Pager *pPager = (Pager *)pHandle;
57929 	pPager->xPageReload = xPageReload;
57930 }
57931 /*
57932  * Log an error.
57933  * Refer to the declaration of the [Pager] structure
57934  */
unqliteKvIoErr(unqlite_kv_handle pHandle,const char * zErr)57935 static void unqliteKvIoErr(unqlite_kv_handle pHandle,const char *zErr)
57936 {
57937 	Pager *pPager = (Pager *)pHandle;
57938 	unqliteGenError(pPager->pDb,zErr);
57939 }
57940 /*
57941  * Init an instance of the [unqlite_kv_io] structure.
57942  */
pager_kv_io_init(Pager * pPager,unqlite_kv_methods * pMethods,unqlite_kv_io * pIo)57943 static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo)
57944 {
57945 	pIo->pHandle =  pPager;
57946 	pIo->pMethods = pMethods;
57947 
57948 	pIo->xGet    = unqliteKvIoPageGet;
57949 	pIo->xLookup = unqliteKvIoPageLookup;
57950 	pIo->xNew    = unqliteKvIoNewPage;
57951 
57952 	pIo->xWrite     = unqliteKvIopageWrite;
57953 	pIo->xDontWrite = unqliteKvIoPageDontWrite;
57954 	pIo->xDontJournal = unqliteKvIoPageDontJournal;
57955 	pIo->xDontMkHot = unqliteKvIoPageDontMakeHot;
57956 
57957 	pIo->xPageRef   = unqliteKvIopage_ref;
57958 	pIo->xPageUnref = unqliteKvIoPageUnRef;
57959 
57960 	pIo->xPageSize = unqliteKvIoPageSize;
57961 	pIo->xReadOnly = unqliteKvIoReadOnly;
57962 
57963 	pIo->xTmpPage =  unqliteKvIoTempPage;
57964 
57965 	pIo->xSetUnpin = unqliteKvIoPageUnpin;
57966 	pIo->xSetReload = unqliteKvIoPageReload;
57967 
57968 	pIo->xErr = unqliteKvIoErr;
57969 
57970 	return UNQLITE_OK;
57971 }
57972 
57973 /* unqlite_jx9.c */
57974 /*
57975  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
57976  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
57977  * Version 1.1.6
57978  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
57979  * please contact Symisc Systems via:
57980  *       legal@symisc.net
57981  *       licensing@symisc.net
57982  *       contact@symisc.net
57983  * or visit:
57984  *      http://unqlite.org/licensing.html
57985  */
57986  /* $SymiscID: unql_jx9.c v1.2 FreeBSD 2013-01-24 22:45 stable <chm@symisc.net> $ */
57987 #ifndef UNQLITE_AMALGAMATION
57988 #include "unqliteInt.h"
57989 #endif
57990 /*
57991  * This file implements UnQLite functions (db_exists(), db_create(), db_put(), db_get(), etc.) for the
57992  * underlying Jx9 Virtual Machine.
57993  */
57994 /*
57995  * string db_version(void)
57996  *   Return the current version of the unQLite database engine.
57997  * Parameter
57998  *   None
57999  * Return
58000  *    unQLite version number (string).
58001  */
unqliteBuiltin_db_version(jx9_context * pCtx,int argc,jx9_value ** argv)58002 static int unqliteBuiltin_db_version(jx9_context *pCtx,int argc,jx9_value **argv)
58003 {
58004 	SXUNUSED(argc); /* cc warning */
58005 	SXUNUSED(argv);
58006 	jx9_result_string(pCtx,UNQLITE_VERSION,(int)sizeof(UNQLITE_VERSION)-1);
58007 	return JX9_OK;
58008 }
58009 /*
58010  * string db_errlog(void)
58011  *   Return the database error log.
58012  * Parameter
58013  *   None
58014  * Return
58015  *    Database error log (string).
58016  */
unqliteBuiltin_db_errlog(jx9_context * pCtx,int argc,jx9_value ** argv)58017 static int unqliteBuiltin_db_errlog(jx9_context *pCtx,int argc,jx9_value **argv)
58018 {
58019 	unqlite_vm *pVm;
58020 	SyBlob *pErr;
58021 
58022 	SXUNUSED(argc); /* cc warning */
58023 	SXUNUSED(argv);
58024 
58025 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58026 	/* Point to the error log */
58027 	pErr = &pVm->pDb->sErr;
58028 	/* Return the log */
58029 	jx9_result_string(pCtx,(const char *)SyBlobData(pErr),(int)SyBlobLength(pErr));
58030 	return JX9_OK;
58031 }
58032 /*
58033  * string db_copyright(void)
58034  * string db_credits(void)
58035  *   Return the unQLite database engine copyright notice.
58036  * Parameter
58037  *   None
58038  * Return
58039  *    Copyright notice.
58040  */
unqliteBuiltin_db_credits(jx9_context * pCtx,int argc,jx9_value ** argv)58041 static int unqliteBuiltin_db_credits(jx9_context *pCtx,int argc,jx9_value **argv)
58042 {
58043 	SXUNUSED(argc); /* cc warning */
58044 	SXUNUSED(argv);
58045 	jx9_result_string(pCtx,UNQLITE_COPYRIGHT,(int)sizeof(UNQLITE_COPYRIGHT)-1);
58046 	return JX9_OK;
58047 }
58048 /*
58049  * string db_sig(void)
58050  *   Return the unQLite database engine unique signature.
58051  * Parameter
58052  *   None
58053  * Return
58054  *    unQLite signature.
58055  */
unqliteBuiltin_db_sig(jx9_context * pCtx,int argc,jx9_value ** argv)58056 static int unqliteBuiltin_db_sig(jx9_context *pCtx,int argc,jx9_value **argv)
58057 {
58058 	SXUNUSED(argc); /* cc warning */
58059 	SXUNUSED(argv);
58060 	jx9_result_string(pCtx,UNQLITE_IDENT,sizeof(UNQLITE_IDENT)-1);
58061 	return JX9_OK;
58062 }
58063 /*
58064  * bool collection_exists(string $name)
58065  * bool db_exits(string $name)
58066  *   Check if a given collection exists in the underlying database.
58067  * Parameter
58068  *   name: Lookup name
58069  * Return
58070  *    TRUE if the collection exits. FALSE otherwise.
58071  */
unqliteBuiltin_collection_exists(jx9_context * pCtx,int argc,jx9_value ** argv)58072 static int unqliteBuiltin_collection_exists(jx9_context *pCtx,int argc,jx9_value **argv)
58073 {
58074 	unqlite_col *pCol;
58075 	const char *zName;
58076 	unqlite_vm *pVm;
58077 	SyString sName;
58078 	int nByte;
58079 	/* Extract collection name */
58080 	if( argc < 1 ){
58081 		/* Missing arguments */
58082 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58083 		/* Return false */
58084 		jx9_result_bool(pCtx,0);
58085 		return JX9_OK;
58086 	}
58087 	zName = jx9_value_to_string(argv[0],&nByte);
58088 	if( nByte < 1){
58089 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58090 		/* Return false */
58091 		jx9_result_bool(pCtx,0);
58092 		return JX9_OK;
58093 	}
58094 	SyStringInitFromBuf(&sName,zName,nByte);
58095 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58096 	/* Perform the lookup */
58097 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58098 	/* Lookup result */
58099 	jx9_result_bool(pCtx,pCol ? 1 : 0);
58100 	return JX9_OK;
58101 }
58102 /*
58103  * bool collection_create(string $name)
58104  * bool db_create(string $name)
58105  *   Create a new collection.
58106  * Parameter
58107  *   name: Collection name
58108  * Return
58109  *    TRUE if the collection was successfuly created. FALSE otherwise.
58110  */
unqliteBuiltin_collection_create(jx9_context * pCtx,int argc,jx9_value ** argv)58111 static int unqliteBuiltin_collection_create(jx9_context *pCtx,int argc,jx9_value **argv)
58112 {
58113 	const char *zName;
58114 	unqlite_vm *pVm;
58115 	SyString sName;
58116 	int nByte;
58117 	int rc;
58118 	/* Extract collection name */
58119 	if( argc < 1 ){
58120 		/* Missing arguments */
58121 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58122 		/* Return false */
58123 		jx9_result_bool(pCtx,0);
58124 		return JX9_OK;
58125 	}
58126 	zName = jx9_value_to_string(argv[0],&nByte);
58127 	if( nByte < 1){
58128 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58129 		/* Return false */
58130 		jx9_result_bool(pCtx,0);
58131 		return JX9_OK;
58132 	}
58133 	SyStringInitFromBuf(&sName,zName,nByte);
58134 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58135 	/* Try to create the collection */
58136 	rc = unqliteCreateCollection(pVm,&sName);
58137 	/* Return the result to the caller */
58138 	jx9_result_bool(pCtx,rc == UNQLITE_OK ? 1 : 0);
58139 	return JX9_OK;
58140 }
58141 /*
58142  * value db_fetch(string $col_name)
58143  * value db_get(string $col_name)
58144  *   Fetch the current record from a given collection and advance
58145  *   the record cursor.
58146  * Parameter
58147  *   col_name: Collection name
58148  * Return
58149  *    Record content success. NULL on failure (No more records to retrieve).
58150  */
unqliteBuiltin_db_fetch_next(jx9_context * pCtx,int argc,jx9_value ** argv)58151 static int unqliteBuiltin_db_fetch_next(jx9_context *pCtx,int argc,jx9_value **argv)
58152 {
58153 	unqlite_col *pCol;
58154 	const char *zName;
58155 	unqlite_vm *pVm;
58156 	SyString sName;
58157 	int nByte;
58158 	int rc;
58159 	/* Extract collection name */
58160 	if( argc < 1 ){
58161 		/* Missing arguments */
58162 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58163 		/* Return null */
58164 		jx9_result_null(pCtx);
58165 		return JX9_OK;
58166 	}
58167 	zName = jx9_value_to_string(argv[0],&nByte);
58168 	if( nByte < 1){
58169 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58170 		/* Return null */
58171 		jx9_result_null(pCtx);
58172 		return JX9_OK;
58173 	}
58174 	SyStringInitFromBuf(&sName,zName,nByte);
58175 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58176 	/* Fetch the collection */
58177 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58178 	if( pCol ){
58179 		/* Fetch the current record */
58180 		jx9_value *pValue;
58181 		pValue = jx9_context_new_scalar(pCtx);
58182 		if( pValue == 0 ){
58183 			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
58184 			jx9_result_null(pCtx);
58185 			return JX9_OK;
58186 		}else{
58187 			rc = unqliteCollectionFetchNextRecord(pCol,pValue);
58188 			if( rc == UNQLITE_OK ){
58189 				jx9_result_value(pCtx,pValue);
58190 				/* pValue will be automatically released as soon we return from this function */
58191 			}else{
58192 				/* Return null */
58193 				jx9_result_null(pCtx);
58194 			}
58195 		}
58196 	}else{
58197 		/* No such collection, return null */
58198 		jx9_result_null(pCtx);
58199 	}
58200 	return JX9_OK;
58201 }
58202 /*
58203  * value db_fetch_by_id(string $col_name,int64 $record_id)
58204  * value db_get_by_id(string $col_name,int64 $record_id)
58205  *   Fetch a record using its unique ID from a given collection.
58206  * Parameter
58207  *   col_name:  Collection name
58208  *   record_id: Record number (__id field of a JSON object)
58209  * Return
58210  *    Record content success. NULL on failure (No such record).
58211  */
unqliteBuiltin_db_fetch_by_id(jx9_context * pCtx,int argc,jx9_value ** argv)58212 static int unqliteBuiltin_db_fetch_by_id(jx9_context *pCtx,int argc,jx9_value **argv)
58213 {
58214 	unqlite_col *pCol;
58215 	const char *zName;
58216 	unqlite_vm *pVm;
58217 	SyString sName;
58218 	jx9_int64 nId;
58219 	int nByte;
58220 	int rc;
58221 	/* Extract collection name */
58222 	if( argc < 2 ){
58223 		/* Missing arguments */
58224 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or record ID");
58225 		/* Return NULL */
58226 		jx9_result_null(pCtx);
58227 		return JX9_OK;
58228 	}
58229 	zName = jx9_value_to_string(argv[0],&nByte);
58230 	if( nByte < 1){
58231 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58232 		/* Return NULL */
58233 		jx9_result_null(pCtx);
58234 		return JX9_OK;
58235 	}
58236 	/* Extract the record ID */
58237 	nId = jx9_value_to_int(argv[1]);
58238 	SyStringInitFromBuf(&sName,zName,nByte);
58239 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58240 	/* Fetch the collection */
58241 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58242 	if( pCol ){
58243 		/* Fetch the desired record */
58244 		jx9_value *pValue;
58245 		pValue = jx9_context_new_scalar(pCtx);
58246 		if( pValue == 0 ){
58247 			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
58248 			jx9_result_null(pCtx);
58249 			return JX9_OK;
58250 		}else{
58251 			rc = unqliteCollectionFetchRecordById(pCol,nId,pValue);
58252 			if( rc == UNQLITE_OK ){
58253 				jx9_result_value(pCtx,pValue);
58254 				/* pValue will be automatically released as soon we return from this function */
58255 			}else{
58256 				/* No such record, return null */
58257 				jx9_result_null(pCtx);
58258 			}
58259 		}
58260 	}else{
58261 		/* No such collection, return null */
58262 		jx9_result_null(pCtx);
58263 	}
58264 	return JX9_OK;
58265 }
58266 /*
58267  * array db_fetch_all(string $col_name,[callback filter_callback])
58268  * array db_get_all(string $col_name,[callback filter_callback])
58269  *   Retrieve all records of a given collection and apply the given
58270  *   callback if available to filter records.
58271  * Parameter
58272  *   col_name: Collection name
58273  * Return
58274  *    Contents of the collection (JSON array) on success. NULL on failure.
58275  */
unqliteBuiltin_db_fetch_all(jx9_context * pCtx,int argc,jx9_value ** argv)58276 static int unqliteBuiltin_db_fetch_all(jx9_context *pCtx,int argc,jx9_value **argv)
58277 {
58278 	unqlite_col *pCol;
58279 	const char *zName;
58280 	unqlite_vm *pVm;
58281 	SyString sName;
58282 	int nByte;
58283 	int rc;
58284 	/* Extract collection name */
58285 	if( argc < 1 ){
58286 		/* Missing arguments */
58287 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58288 		/* Return NULL */
58289 		jx9_result_null(pCtx);
58290 		return JX9_OK;
58291 	}
58292 	zName = jx9_value_to_string(argv[0],&nByte);
58293 	if( nByte < 1){
58294 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58295 		/* Return NULL */
58296 		jx9_result_null(pCtx);
58297 		return JX9_OK;
58298 	}
58299 	SyStringInitFromBuf(&sName,zName,nByte);
58300 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58301 	/* Fetch the collection */
58302 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58303 	if( pCol ){
58304 		jx9_value *pValue,*pArray,*pCallback = 0;
58305 		jx9_value sResult; /* Callback result */
58306 		/* Allocate an empty scalar value and an empty JSON array */
58307 		pArray = jx9_context_new_array(pCtx);
58308 		pValue = jx9_context_new_scalar(pCtx);
58309 		jx9MemObjInit(pCtx->pVm,&sResult);
58310 		if( pValue == 0 || pArray == 0 ){
58311 			jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
58312 			jx9_result_null(pCtx);
58313 			return JX9_OK;
58314 		}
58315 		if( argc > 1 && jx9_value_is_callable(argv[1]) ){
58316 			pCallback = argv[1];
58317 		}
58318 		unqliteCollectionResetRecordCursor(pCol);
58319 		/* Fetch collection records one after one */
58320 		while( UNQLITE_OK == unqliteCollectionFetchNextRecord(pCol,pValue) ){
58321 			if( pCallback ){
58322 				jx9_value *apArg[2];
58323 				/* Invoke the filter callback */
58324 				apArg[0] = pValue;
58325 				rc = jx9VmCallUserFunction(pCtx->pVm,pCallback,1,apArg,&sResult);
58326 				if( rc == JX9_OK ){
58327 					int iResult; /* Callback result */
58328 					/* Extract callback result */
58329 					iResult = jx9_value_to_bool(&sResult);
58330 					if( !iResult ){
58331 						/* Discard the result */
58332 						unqliteCollectionCacheRemoveRecord(pCol,unqliteCollectionCurrentRecordId(pCol) - 1);
58333 						continue;
58334 					}
58335 				}
58336 			}
58337 			/* Put the value in the JSON array */
58338 			jx9_array_add_elem(pArray,0,pValue);
58339 			/* Release the value */
58340 			jx9_value_null(pValue);
58341 		}
58342 		jx9MemObjRelease(&sResult);
58343 		/* Finally, return our array */
58344 		jx9_result_value(pCtx,pArray);
58345 		/* pValue will be automatically released as soon we return from
58346 		 * this foreign function.
58347 		 */
58348 	}else{
58349 		/* No such collection, return null */
58350 		jx9_result_null(pCtx);
58351 	}
58352 	return JX9_OK;
58353 }
58354 /*
58355  * int64 db_last_record_id(string $col_name)
58356  *   Return the ID of the last inserted record.
58357  * Parameter
58358  *   col_name: Collection name
58359  * Return
58360  *    Record ID (64-bit integer) on success. FALSE on failure.
58361  */
unqliteBuiltin_db_last_record_id(jx9_context * pCtx,int argc,jx9_value ** argv)58362 static int unqliteBuiltin_db_last_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
58363 {
58364 	unqlite_col *pCol;
58365 	const char *zName;
58366 	unqlite_vm *pVm;
58367 	SyString sName;
58368 	int nByte;
58369 	/* Extract collection name */
58370 	if( argc < 1 ){
58371 		/* Missing arguments */
58372 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58373 		/* Return false */
58374 		jx9_result_bool(pCtx,0);
58375 		return JX9_OK;
58376 	}
58377 	zName = jx9_value_to_string(argv[0],&nByte);
58378 	if( nByte < 1){
58379 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58380 		/* Return false */
58381 		jx9_result_bool(pCtx,0);
58382 		return JX9_OK;
58383 	}
58384 	SyStringInitFromBuf(&sName,zName,nByte);
58385 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58386 	/* Fetch the collection */
58387 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58388 	if( pCol ){
58389 		jx9_result_int64(pCtx,unqliteCollectionLastRecordId(pCol));
58390 	}else{
58391 		/* No such collection, return FALSE */
58392 		jx9_result_bool(pCtx,0);
58393 	}
58394 	return JX9_OK;
58395 }
58396 /*
58397  * inr64 db_current_record_id(string $col_name)
58398  *   Return the current record ID.
58399  * Parameter
58400  *   col_name: Collection name
58401  * Return
58402  *    Current record ID (64-bit integer) on success. FALSE on failure.
58403  */
unqliteBuiltin_db_current_record_id(jx9_context * pCtx,int argc,jx9_value ** argv)58404 static int unqliteBuiltin_db_current_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
58405 {
58406 	unqlite_col *pCol;
58407 	const char *zName;
58408 	unqlite_vm *pVm;
58409 	SyString sName;
58410 	int nByte;
58411 	/* Extract collection name */
58412 	if( argc < 1 ){
58413 		/* Missing arguments */
58414 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58415 		/* Return false */
58416 		jx9_result_bool(pCtx,0);
58417 		return JX9_OK;
58418 	}
58419 	zName = jx9_value_to_string(argv[0],&nByte);
58420 	if( nByte < 1){
58421 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58422 		/* Return false */
58423 		jx9_result_bool(pCtx,0);
58424 		return JX9_OK;
58425 	}
58426 	SyStringInitFromBuf(&sName,zName,nByte);
58427 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58428 	/* Fetch the collection */
58429 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58430 	if( pCol ){
58431 		jx9_result_int64(pCtx,unqliteCollectionCurrentRecordId(pCol));
58432 	}else{
58433 		/* No such collection, return FALSE */
58434 		jx9_result_bool(pCtx,0);
58435 	}
58436 	return JX9_OK;
58437 }
58438 /*
58439  * bool db_reset_record_cursor(string $col_name)
58440  *   Reset the record ID cursor.
58441  * Parameter
58442  *   col_name: Collection name
58443  * Return
58444  *    TRUE on success. FALSE on failure.
58445  */
unqliteBuiltin_db_reset_record_cursor(jx9_context * pCtx,int argc,jx9_value ** argv)58446 static int unqliteBuiltin_db_reset_record_cursor(jx9_context *pCtx,int argc,jx9_value **argv)
58447 {
58448 	unqlite_col *pCol;
58449 	const char *zName;
58450 	unqlite_vm *pVm;
58451 	SyString sName;
58452 	int nByte;
58453 	/* Extract collection name */
58454 	if( argc < 1 ){
58455 		/* Missing arguments */
58456 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58457 		/* Return false */
58458 		jx9_result_bool(pCtx,0);
58459 		return JX9_OK;
58460 	}
58461 	zName = jx9_value_to_string(argv[0],&nByte);
58462 	if( nByte < 1){
58463 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58464 		/* Return false */
58465 		jx9_result_bool(pCtx,0);
58466 		return JX9_OK;
58467 	}
58468 	SyStringInitFromBuf(&sName,zName,nByte);
58469 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58470 	/* Fetch the collection */
58471 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58472 	if( pCol ){
58473 		unqliteCollectionResetRecordCursor(pCol);
58474 		jx9_result_bool(pCtx,1);
58475 	}else{
58476 		/* No such collection */
58477 		jx9_result_bool(pCtx,0);
58478 	}
58479 	return JX9_OK;
58480 }
58481 /*
58482  * int64 db_total_records(string $col_name)
58483  *   Return the total number of inserted records in the given collection.
58484  * Parameter
58485  *   col_name: Collection name
58486  * Return
58487  *    Total number of records on success. FALSE on failure.
58488  */
unqliteBuiltin_db_total_records(jx9_context * pCtx,int argc,jx9_value ** argv)58489 static int unqliteBuiltin_db_total_records(jx9_context *pCtx,int argc,jx9_value **argv)
58490 {
58491 	unqlite_col *pCol;
58492 	const char *zName;
58493 	unqlite_vm *pVm;
58494 	SyString sName;
58495 	int nByte;
58496 	/* Extract collection name */
58497 	if( argc < 1 ){
58498 		/* Missing arguments */
58499 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58500 		/* Return false */
58501 		jx9_result_bool(pCtx,0);
58502 		return JX9_OK;
58503 	}
58504 	zName = jx9_value_to_string(argv[0],&nByte);
58505 	if( nByte < 1){
58506 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58507 		/* Return false */
58508 		jx9_result_bool(pCtx,0);
58509 		return JX9_OK;
58510 	}
58511 	SyStringInitFromBuf(&sName,zName,nByte);
58512 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58513 	/* Fetch the collection */
58514 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58515 	if( pCol ){
58516 		unqlite_int64 nRec;
58517 		nRec = unqliteCollectionTotalRecords(pCol);
58518 		jx9_result_int64(pCtx,nRec);
58519 	}else{
58520 		/* No such collection */
58521 		jx9_result_bool(pCtx,0);
58522 	}
58523 	return JX9_OK;
58524 }
58525 /*
58526  * string db_creation_date(string $col_name)
58527  *   Return the creation date of the given collection.
58528  * Parameter
58529  *   col_name: Collection name
58530  * Return
58531  *    Creation date on success. FALSE on failure.
58532  */
unqliteBuiltin_db_creation_date(jx9_context * pCtx,int argc,jx9_value ** argv)58533 static int unqliteBuiltin_db_creation_date(jx9_context *pCtx,int argc,jx9_value **argv)
58534 {
58535 	unqlite_col *pCol;
58536 	const char *zName;
58537 	unqlite_vm *pVm;
58538 	SyString sName;
58539 	int nByte;
58540 	/* Extract collection name */
58541 	if( argc < 1 ){
58542 		/* Missing arguments */
58543 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58544 		/* Return false */
58545 		jx9_result_bool(pCtx,0);
58546 		return JX9_OK;
58547 	}
58548 	zName = jx9_value_to_string(argv[0],&nByte);
58549 	if( nByte < 1){
58550 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58551 		/* Return false */
58552 		jx9_result_bool(pCtx,0);
58553 		return JX9_OK;
58554 	}
58555 	SyStringInitFromBuf(&sName,zName,nByte);
58556 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58557 	/* Fetch the collection */
58558 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58559 	if( pCol ){
58560 		Sytm *pTm = &pCol->sCreation;
58561 		jx9_result_string_format(pCtx,"%d-%d-%d %02d:%02d:%02d",
58562 			pTm->tm_year,pTm->tm_mon,pTm->tm_mday,
58563 			pTm->tm_hour,pTm->tm_min,pTm->tm_sec
58564 			);
58565 	}else{
58566 		/* No such collection */
58567 		jx9_result_bool(pCtx,0);
58568 	}
58569 	return JX9_OK;
58570 }
58571 /*
58572  * bool db_store(string $col_name,...)
58573  * bool db_put(string $col_name,...)
58574  *   Store one or more JSON values in a given collection.
58575  * Parameter
58576  *   col_name: Collection name
58577  * Return
58578  *    TRUE on success. FALSE on failure.
58579  */
unqliteBuiltin_db_store(jx9_context * pCtx,int argc,jx9_value ** argv)58580 static int unqliteBuiltin_db_store(jx9_context *pCtx,int argc,jx9_value **argv)
58581 {
58582 	unqlite_col *pCol;
58583 	const char *zName;
58584 	unqlite_vm *pVm;
58585 	SyString sName;
58586 	int nByte;
58587 	int rc;
58588 	int i;
58589 	/* Extract collection name */
58590 	if( argc < 2 ){
58591 		/* Missing arguments */
58592 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
58593 		/* Return false */
58594 		jx9_result_bool(pCtx,0);
58595 		return JX9_OK;
58596 	}
58597 	zName = jx9_value_to_string(argv[0],&nByte);
58598 	if( nByte < 1){
58599 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58600 		/* Return false */
58601 		jx9_result_bool(pCtx,0);
58602 		return JX9_OK;
58603 	}
58604 	SyStringInitFromBuf(&sName,zName,nByte);
58605 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58606 	/* Fetch the collection */
58607 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58608 	if( pCol == 0 ){
58609 		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
58610 		/* Return false */
58611 		jx9_result_bool(pCtx,0);
58612 		return JX9_OK;
58613 	}
58614 	/* Store the given values */
58615 	for( i = 1 ; i < argc ; ++i ){
58616 		rc = unqliteCollectionPut(pCol,argv[i],0);
58617 		if( rc != UNQLITE_OK){
58618 			jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,
58619 				"Error while storing record %d in collection '%z'",i,&sName
58620 				);
58621 			/* Return false */
58622 			jx9_result_bool(pCtx,0);
58623 			return JX9_OK;
58624 		}
58625 	}
58626 	/* All done, return TRUE */
58627 	jx9_result_bool(pCtx,1);
58628 	return JX9_OK;
58629 }
58630 /*
58631  * bool db_drop_collection(string $col_name)
58632  * bool collection_delete(string $col_name)
58633  *   Remove a given collection from the database.
58634  * Parameter
58635  *   col_name: Collection name
58636  * Return
58637  *    TRUE on success. FALSE on failure.
58638  */
unqliteBuiltin_db_drop_col(jx9_context * pCtx,int argc,jx9_value ** argv)58639 static int unqliteBuiltin_db_drop_col(jx9_context *pCtx,int argc,jx9_value **argv)
58640 {
58641 	unqlite_col *pCol;
58642 	const char *zName;
58643 	unqlite_vm *pVm;
58644 	SyString sName;
58645 	int nByte;
58646 	int rc;
58647 	/* Extract collection name */
58648 	if( argc < 1 ){
58649 		/* Missing arguments */
58650 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
58651 		/* Return false */
58652 		jx9_result_bool(pCtx,0);
58653 		return JX9_OK;
58654 	}
58655 	zName = jx9_value_to_string(argv[0],&nByte);
58656 	if( nByte < 1){
58657 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58658 		/* Return false */
58659 		jx9_result_bool(pCtx,0);
58660 		return JX9_OK;
58661 	}
58662 	SyStringInitFromBuf(&sName,zName,nByte);
58663 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58664 	/* Fetch the collection */
58665 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58666 	if( pCol == 0 ){
58667 		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
58668 		/* Return false */
58669 		jx9_result_bool(pCtx,0);
58670 		return JX9_OK;
58671 	}
58672 	/* Drop the collection */
58673 	rc = unqliteDropCollection(pCol);
58674 	/* Processing result */
58675 	jx9_result_bool(pCtx,rc == UNQLITE_OK);
58676 	return JX9_OK;
58677 }
58678 /*
58679  * bool db_drop_record(string $col_name,int64 record_id)
58680  *   Remove a given record from a collection.
58681  * Parameter
58682  *   col_name: Collection name.
58683  *   record_id: ID of the record.
58684  * Return
58685  *    TRUE on success. FALSE on failure.
58686  */
unqliteBuiltin_db_drop_record(jx9_context * pCtx,int argc,jx9_value ** argv)58687 static int unqliteBuiltin_db_drop_record(jx9_context *pCtx,int argc,jx9_value **argv)
58688 {
58689 	unqlite_col *pCol;
58690 	const char *zName;
58691 	unqlite_vm *pVm;
58692 	SyString sName;
58693 	jx9_int64 nId;
58694 	int nByte;
58695 	int rc;
58696 	/* Extract collection name */
58697 	if( argc < 2 ){
58698 		/* Missing arguments */
58699 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
58700 		/* Return false */
58701 		jx9_result_bool(pCtx,0);
58702 		return JX9_OK;
58703 	}
58704 	zName = jx9_value_to_string(argv[0],&nByte);
58705 	if( nByte < 1){
58706 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58707 		/* Return false */
58708 		jx9_result_bool(pCtx,0);
58709 		return JX9_OK;
58710 	}
58711 	SyStringInitFromBuf(&sName,zName,nByte);
58712 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58713 	/* Fetch the collection */
58714 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58715 	if( pCol == 0 ){
58716 		jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
58717 		/* Return false */
58718 		jx9_result_bool(pCtx,0);
58719 		return JX9_OK;
58720 	}
58721 	/* Extract the record ID */
58722 	nId = jx9_value_to_int64(argv[1]);
58723 	/* Drop the record */
58724 	rc = unqliteCollectionDropRecord(pCol,nId,1,1);
58725 	/* Processing result */
58726 	jx9_result_bool(pCtx,rc == UNQLITE_OK);
58727 	return JX9_OK;
58728 }
58729 /*
58730  * bool db_update_record(string $col_name, int_64 record_id, object $json_object)
58731  *   Update a given record with new json object
58732  * Parameter
58733  * col_name: Collection name
58734  *   record_id: ID of the record
58735  *   json_object: New Record data
58736  * Return
58737  *   TRUE on success. FALSE on failure.
58738  */
unqliteBuiltin_db_update_record(jx9_context * pCtx,int argc,jx9_value ** argv)58739 static int unqliteBuiltin_db_update_record(jx9_context *pCtx,int argc,jx9_value **argv)
58740 {
58741     unqlite_col *pCol;
58742     const char *zName;
58743     unqlite_vm *pVm;
58744     SyString sName;
58745     jx9_int64 nId;
58746     int nByte;
58747     int rc;
58748     /* Extract collection name */
58749     if( argc < 2 ){
58750         /* Missing arguments */
58751         jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
58752         /* Return false */
58753         jx9_result_bool(pCtx,0);
58754         return JX9_OK;
58755     }
58756     zName = jx9_value_to_string(argv[0],&nByte);
58757     if( nByte < 1){
58758         jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58759         /* Return false */
58760         jx9_result_bool(pCtx,0);
58761         return JX9_OK;
58762     }
58763     SyStringInitFromBuf(&sName,zName,nByte);
58764     pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58765     /* Fetch the collection */
58766     pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58767     if( pCol == 0 ){
58768         jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
58769         /* Return false */
58770         jx9_result_bool(pCtx,0);
58771         return JX9_OK;
58772     }
58773     /* Update a record with the given value */
58774     nId = jx9_value_to_int64(argv[1]);
58775     rc = unqliteCollectionUpdateRecord(pCol, nId, argv[2], 0);
58776     /* All done, return TRUE */
58777     jx9_result_bool(pCtx,rc == UNQLITE_OK);
58778     return JX9_OK;
58779 }
58780 /*
58781  * bool db_set_schema(string $col_name, object $json_object)
58782  *   Set a schema for a given collection.
58783  * Parameter
58784  *   col_name: Collection name.
58785  *   json_object: Collection schema (Must be a JSON object).
58786  * Return
58787  *    TRUE on success. FALSE on failure.
58788  */
unqliteBuiltin_db_set_schema(jx9_context * pCtx,int argc,jx9_value ** argv)58789 static int unqliteBuiltin_db_set_schema(jx9_context *pCtx,int argc,jx9_value **argv)
58790 {
58791 	unqlite_col *pCol;
58792 	const char *zName;
58793 	unqlite_vm *pVm;
58794 	SyString sName;
58795 	int nByte;
58796 	int rc;
58797 	/* Extract collection name */
58798 	if( argc < 2 ){
58799 		/* Missing arguments */
58800 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
58801 		/* Return false */
58802 		jx9_result_bool(pCtx,0);
58803 		return JX9_OK;
58804 	}
58805 	if( !jx9_value_is_json_object(argv[1]) ){
58806 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection scheme");
58807 		/* Return false */
58808 		jx9_result_bool(pCtx,0);
58809 		return JX9_OK;
58810 	}
58811 	zName = jx9_value_to_string(argv[0],&nByte);
58812 	if( nByte < 1){
58813 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58814 		/* Return false */
58815 		jx9_result_bool(pCtx,0);
58816 		return JX9_OK;
58817 	}
58818 	SyStringInitFromBuf(&sName,zName,nByte);
58819 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58820 	/* Fetch the collection */
58821 	rc = UNQLITE_NOOP;
58822 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58823 	if( pCol ){
58824 		/* Set the collection scheme */
58825 		rc = unqliteCollectionSetSchema(pCol,argv[1]);
58826 	}else{
58827 		jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
58828 			"No such collection '%z'",
58829 			&sName
58830 			);
58831 	}
58832 	/* Processing result */
58833 	jx9_result_bool(pCtx,rc == UNQLITE_OK);
58834 	return JX9_OK;
58835 }
58836 /*
58837  * object db_get_schema(string $col_name)
58838  *   Return the schema associated with a given collection.
58839  * Parameter
58840  *   col_name: Collection name
58841  * Return
58842  *    Collection schema on success. null otherwise.
58843  */
unqliteBuiltin_db_get_schema(jx9_context * pCtx,int argc,jx9_value ** argv)58844 static int unqliteBuiltin_db_get_schema(jx9_context *pCtx,int argc,jx9_value **argv)
58845 {
58846 	unqlite_col *pCol;
58847 	const char *zName;
58848 	unqlite_vm *pVm;
58849 	SyString sName;
58850 	int nByte;
58851 	/* Extract collection name */
58852 	if( argc < 1 ){
58853 		/* Missing arguments */
58854 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
58855 		/* Return false */
58856 		jx9_result_bool(pCtx,0);
58857 		return JX9_OK;
58858 	}
58859 	zName = jx9_value_to_string(argv[0],&nByte);
58860 	if( nByte < 1){
58861 		jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
58862 		/* Return false */
58863 		jx9_result_bool(pCtx,0);
58864 		return JX9_OK;
58865 	}
58866 	SyStringInitFromBuf(&sName,zName,nByte);
58867 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58868 	/* Fetch the collection */
58869 	pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
58870 	if( pCol ){
58871 		/* Return the collection schema */
58872 		jx9_result_value(pCtx,&pCol->sSchema);
58873 	}else{
58874 		jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
58875 			"No such collection '%z'",
58876 			&sName
58877 			);
58878 		jx9_result_null(pCtx);
58879 	}
58880 	return JX9_OK;
58881 }
58882 /*
58883  * bool db_begin(void)
58884  *   Manually begin a write transaction.
58885  * Parameter
58886  *   None
58887  * Return
58888  *    TRUE on success. FALSE otherwise.
58889  */
unqliteBuiltin_db_begin(jx9_context * pCtx,int argc,jx9_value ** argv)58890 static int unqliteBuiltin_db_begin(jx9_context *pCtx,int argc,jx9_value **argv)
58891 {
58892 	unqlite_vm *pVm;
58893 	unqlite *pDb;
58894 	int rc;
58895 	SXUNUSED(argc); /* cc warning */
58896 	SXUNUSED(argv);
58897 	/* Point to the unqlite Vm */
58898 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58899 	/* Point to the underlying database handle  */
58900 	pDb = pVm->pDb;
58901 	/* Begin the transaction */
58902 	rc = unqlitePagerBegin(pDb->sDB.pPager);
58903 	/* result */
58904 	jx9_result_bool(pCtx,rc == UNQLITE_OK );
58905 	return JX9_OK;
58906 }
58907 /*
58908  * bool db_commit(void)
58909  *   Manually commit a transaction.
58910  * Parameter
58911  *   None
58912  * Return
58913  *    TRUE if the transaction was successfuly commited. FALSE otherwise.
58914  */
unqliteBuiltin_db_commit(jx9_context * pCtx,int argc,jx9_value ** argv)58915 static int unqliteBuiltin_db_commit(jx9_context *pCtx,int argc,jx9_value **argv)
58916 {
58917 	unqlite_vm *pVm;
58918 	unqlite *pDb;
58919 	int rc;
58920 	SXUNUSED(argc); /* cc warning */
58921 	SXUNUSED(argv);
58922 	/* Point to the unqlite Vm */
58923 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58924 	/* Point to the underlying database handle  */
58925 	pDb = pVm->pDb;
58926 	/* Commit the transaction if any */
58927 	rc = unqlitePagerCommit(pDb->sDB.pPager);
58928 	/* Commit result */
58929 	jx9_result_bool(pCtx,rc == UNQLITE_OK );
58930 	return JX9_OK;
58931 }
58932 /*
58933  * bool db_rollback(void)
58934  *   Manually rollback a transaction.
58935  * Parameter
58936  *   None
58937  * Return
58938  *    TRUE if the transaction was successfuly rolled back. FALSE otherwise
58939  */
unqliteBuiltin_db_rollback(jx9_context * pCtx,int argc,jx9_value ** argv)58940 static int unqliteBuiltin_db_rollback(jx9_context *pCtx,int argc,jx9_value **argv)
58941 {
58942 	unqlite_vm *pVm;
58943 	unqlite *pDb;
58944 	int rc;
58945 	SXUNUSED(argc); /* cc warning */
58946 	SXUNUSED(argv);
58947 	/* Point to the unqlite Vm */
58948 	pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
58949 	/* Point to the underlying database handle  */
58950 	pDb = pVm->pDb;
58951 	/* Rollback the transaction if any */
58952 	rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
58953 	/* Rollback result */
58954 	jx9_result_bool(pCtx,rc == UNQLITE_OK );
58955 	return JX9_OK;
58956 }
58957 /*
58958  * Register all the UnQLite foreign functions defined above.
58959  */
unqliteRegisterJx9Functions(unqlite_vm * pVm)58960 UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm)
58961 {
58962 	static const jx9_builtin_func aBuiltin[] = {
58963 		{ "db_version" , unqliteBuiltin_db_version },
58964 		{ "db_copyright", unqliteBuiltin_db_credits },
58965 		{ "db_credits" , unqliteBuiltin_db_credits },
58966 		{ "db_sig" ,     unqliteBuiltin_db_sig     },
58967 		{ "db_errlog",   unqliteBuiltin_db_errlog  },
58968 		{ "collection_exists", unqliteBuiltin_collection_exists },
58969 		{ "db_exists",         unqliteBuiltin_collection_exists },
58970 		{ "collection_create", unqliteBuiltin_collection_create },
58971 		{ "db_create",         unqliteBuiltin_collection_create },
58972 		{ "db_fetch",          unqliteBuiltin_db_fetch_next     },
58973 		{ "db_get",            unqliteBuiltin_db_fetch_next     },
58974 		{ "db_fetch_by_id",    unqliteBuiltin_db_fetch_by_id    },
58975 		{ "db_get_by_id",      unqliteBuiltin_db_fetch_by_id    },
58976 		{ "db_fetch_all",      unqliteBuiltin_db_fetch_all      },
58977 		{ "db_get_all",        unqliteBuiltin_db_fetch_all      },
58978 		{ "db_last_record_id", unqliteBuiltin_db_last_record_id },
58979 		{ "db_current_record_id", unqliteBuiltin_db_current_record_id },
58980 		{ "db_reset_record_cursor", unqliteBuiltin_db_reset_record_cursor },
58981 		{ "db_total_records",  unqliteBuiltin_db_total_records  },
58982 		{ "db_creation_date",  unqliteBuiltin_db_creation_date  },
58983 		{ "db_store",          unqliteBuiltin_db_store          },
58984 		{ "db_put",            unqliteBuiltin_db_store          },
58985 		{ "db_drop_collection", unqliteBuiltin_db_drop_col      },
58986 		{ "collection_delete", unqliteBuiltin_db_drop_col       },
58987 		{ "db_drop_record",    unqliteBuiltin_db_drop_record    },
58988 		{ "db_update_record",  unqliteBuiltin_db_update_record  },
58989 		{ "db_set_schema",     unqliteBuiltin_db_set_schema     },
58990 		{ "db_get_schema",     unqliteBuiltin_db_get_schema     },
58991 		{ "db_begin",          unqliteBuiltin_db_begin          },
58992 		{ "db_commit",         unqliteBuiltin_db_commit         },
58993 		{ "db_rollback",       unqliteBuiltin_db_rollback       },
58994 	};
58995 	int rc = UNQLITE_OK;
58996 	sxu32 n;
58997 	/* Register the unQLite functions defined above in the Jx9 call table */
58998 	for( n = 0 ; n < SX_ARRAYSIZE(aBuiltin) ; ++n ){
58999 		rc = jx9_create_function(pVm->pJx9Vm,aBuiltin[n].zName,aBuiltin[n].xFunc,pVm);
59000 	}
59001 	return rc;
59002 }
59003 
59004 /* unqlite_vm.c */
59005 /*
59006  * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
59007  * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
59008  * Version 1.1.6
59009  * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
59010  * please contact Symisc Systems via:
59011  *       legal@symisc.net
59012  *       licensing@symisc.net
59013  *       contact@symisc.net
59014  * or visit:
59015  *      http://unqlite.org/licensing.html
59016  */
59017  /* $SymiscID: unqlite_vm.c v1.0 Win7 2013-01-29 23:37 stable <chm@symisc.net> $ */
59018 #ifndef UNQLITE_AMALGAMATION
59019 #include "unqliteInt.h"
59020 #endif
59021 /* This file deals with low level stuff related to the unQLite Virtual Machine */
59022 
59023 /* Record ID as a hash value */
59024 #define COL_RECORD_HASH(RID) (RID)
59025 /*
59026  * Fetch a record from a given collection.
59027  */
CollectionCacheFetchRecord(unqlite_col * pCol,jx9_int64 nId)59028 static unqlite_col_record * CollectionCacheFetchRecord(
59029 	unqlite_col *pCol, /* Target collection */
59030 	jx9_int64 nId      /* Unique record ID */
59031 	)
59032 {
59033 	unqlite_col_record *pEntry;
59034 	if( pCol->nRec < 1 ){
59035 		/* Don't bother hashing */
59036 		return 0;
59037 	}
59038 	pEntry = pCol->apRecord[COL_RECORD_HASH(nId) & (pCol->nRecSize - 1)];
59039 	for(;;){
59040 		if( pEntry == 0 ){
59041 			break;
59042 		}
59043 		if( pEntry->nId == nId ){
59044 			/* Record found */
59045 			return pEntry;
59046 		}
59047 		/* Point to the next entry */
59048 		pEntry = pEntry->pNextCol;
59049 
59050 	}
59051 	/* No such record */
59052 	return 0;
59053 }
59054 /*
59055  * Install a freshly created record in a given collection.
59056  */
CollectionCacheInstallRecord(unqlite_col * pCol,jx9_int64 nId,jx9_value * pValue)59057 static int CollectionCacheInstallRecord(
59058 	unqlite_col *pCol, /* Target collection */
59059 	jx9_int64 nId,     /* Unique record ID */
59060 	jx9_value *pValue  /* JSON value */
59061 	)
59062 {
59063 	unqlite_col_record *pRecord;
59064 	sxu32 iBucket;
59065 	/* Fetch the record first */
59066 	pRecord = CollectionCacheFetchRecord(pCol,nId);
59067 	if( pRecord ){
59068 		/* Record already installed, overwrite its old value  */
59069 		jx9MemObjStore(pValue,&pRecord->sValue);
59070 		return UNQLITE_OK;
59071 	}
59072 	/* Allocate a new instance */
59073 	pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record));
59074 	if( pRecord == 0 ){
59075 		return UNQLITE_NOMEM;
59076 	}
59077 	/* Zero the structure */
59078 	SyZero(pRecord,sizeof(unqlite_col_record));
59079 	/* Fill in the structure */
59080 	jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue);
59081 	jx9MemObjStore(pValue,&pRecord->sValue);
59082 	pRecord->nId = nId;
59083 	pRecord->pCol = pCol;
59084 	/* Install in the corresponding bucket */
59085 	iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
59086 	pRecord->pNextCol = pCol->apRecord[iBucket];
59087 	if( pCol->apRecord[iBucket] ){
59088 		pCol->apRecord[iBucket]->pPrevCol = pRecord;
59089 	}
59090 	pCol->apRecord[iBucket] = pRecord;
59091 	/* Link */
59092 	MACRO_LD_PUSH(pCol->pList,pRecord);
59093 	pCol->nRec++;
59094 	if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){
59095 		/* Allocate a new larger table */
59096 		sxu32 nNewSize = pCol->nRecSize << 1;
59097 		unqlite_col_record *pEntry;
59098 		unqlite_col_record **apNew;
59099 		sxu32 n;
59100 
59101 		apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *));
59102 		if( apNew ){
59103 			/* Zero the new table */
59104 			SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *));
59105 			/* Rehash all entries */
59106 			n = 0;
59107 			pEntry = pCol->pList;
59108 			for(;;){
59109 				/* Loop one */
59110 				if( n >= pCol->nRec ){
59111 					break;
59112 				}
59113 				pEntry->pNextCol = pEntry->pPrevCol = 0;
59114 				/* Install in the new bucket */
59115 				iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1);
59116 				pEntry->pNextCol = apNew[iBucket];
59117 				if( apNew[iBucket]  ){
59118 					apNew[iBucket]->pPrevCol = pEntry;
59119 				}
59120 				apNew[iBucket] = pEntry;
59121 				/* Point to the next entry */
59122 				pEntry = pEntry->pNext;
59123 				n++;
59124 			}
59125 			/* Release the old table and reflect the change */
59126 			SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord);
59127 			pCol->apRecord = apNew;
59128 			pCol->nRecSize = nNewSize;
59129 		}
59130 	}
59131 	/* All done */
59132 	return UNQLITE_OK;
59133 }
59134 /*
59135  * Remove a record from the collection table.
59136  */
unqliteCollectionCacheRemoveRecord(unqlite_col * pCol,jx9_int64 nId)59137 UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(
59138 	unqlite_col *pCol, /* Target collection */
59139 	jx9_int64 nId      /* Unique record ID */
59140 	)
59141 {
59142 	unqlite_col_record *pRecord;
59143 	/* Fetch the record first */
59144 	pRecord = CollectionCacheFetchRecord(pCol,nId);
59145 	if( pRecord == 0 ){
59146 		/* No such record */
59147 		return UNQLITE_NOTFOUND;
59148 	}
59149 	if( pRecord->pPrevCol ){
59150 		pRecord->pPrevCol->pNextCol = pRecord->pNextCol;
59151 	}else{
59152 		sxu32 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
59153 		pCol->apRecord[iBucket] = pRecord->pNextCol;
59154 	}
59155 	if( pRecord->pNextCol ){
59156 		pRecord->pNextCol->pPrevCol = pRecord->pPrevCol;
59157 	}
59158 	/* Unlink */
59159 	MACRO_LD_REMOVE(pCol->pList,pRecord);
59160 	pCol->nRec--;
59161 	return UNQLITE_OK;
59162 }
59163 /*
59164  * Discard a collection and its records.
59165  */
CollectionCacheRelease(unqlite_col * pCol)59166 static int CollectionCacheRelease(unqlite_col *pCol)
59167 {
59168 	unqlite_col_record *pNext,*pRec = pCol->pList;
59169 	unqlite_vm *pVm = pCol->pVm;
59170 	sxu32 n;
59171 	/* Discard all records */
59172 	for( n = 0 ; n < pCol->nRec ; ++n ){
59173 		pNext = pRec->pNext;
59174 		jx9MemObjRelease(&pRec->sValue);
59175 		SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec);
59176 		/* Point to the next record */
59177 		pRec = pNext;
59178 	}
59179 	SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
59180 	pCol->nRec = pCol->nRecSize = 0;
59181 	pCol->pList = 0;
59182 	return UNQLITE_OK;
59183 }
59184 /*
59185  * Install a freshly created collection in the unqlite VM.
59186  */
unqliteVmInstallCollection(unqlite_vm * pVm,unqlite_col * pCol)59187 static int unqliteVmInstallCollection(
59188 	unqlite_vm *pVm,  /* Target VM */
59189 	unqlite_col *pCol /* Collection to install */
59190 	)
59191 {
59192 	SyString *pName = &pCol->sName;
59193 	sxu32 iBucket;
59194 	/* Hash the collection name */
59195 	pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte);
59196 	/* Install it in the corresponding bucket */
59197 	iBucket = pCol->nHash & (pVm->iColSize - 1);
59198 	pCol->pNextCol = pVm->apCol[iBucket];
59199 	if( pVm->apCol[iBucket] ){
59200 		pVm->apCol[iBucket]->pPrevCol = pCol;
59201 	}
59202 	pVm->apCol[iBucket] = pCol;
59203 	/* Link to the list of active collections */
59204 	MACRO_LD_PUSH(pVm->pCol,pCol);
59205 	pVm->iCol++;
59206 	if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){
59207 		/* Grow the hashtable */
59208 		sxu32 nNewSize = pVm->iColSize << 1;
59209 		unqlite_col *pEntry;
59210 		unqlite_col **apNew;
59211 		sxu32 n;
59212 
59213 		apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *));
59214 		if( apNew ){
59215 			/* Zero the new table */
59216 			SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *));
59217 			/* Rehash all entries */
59218 			n = 0;
59219 			pEntry = pVm->pCol;
59220 			for(;;){
59221 				/* Loop one */
59222 				if( n >= pVm->iCol ){
59223 					break;
59224 				}
59225 				pEntry->pNextCol = pEntry->pPrevCol = 0;
59226 				/* Install in the new bucket */
59227 				iBucket = pEntry->nHash & (nNewSize - 1);
59228 				pEntry->pNextCol = apNew[iBucket];
59229 				if( apNew[iBucket]  ){
59230 					apNew[iBucket]->pPrevCol = pEntry;
59231 				}
59232 				apNew[iBucket] = pEntry;
59233 				/* Point to the next entry */
59234 				pEntry = pEntry->pNext;
59235 				n++;
59236 			}
59237 			/* Release the old table and reflect the change */
59238 			SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol);
59239 			pVm->apCol = apNew;
59240 			pVm->iColSize  = nNewSize;
59241 		}
59242 	}
59243 	return UNQLITE_OK;
59244 }
59245 /*
59246  * Fetch a collection from the target VM.
59247  */
unqliteVmFetchCollection(unqlite_vm * pVm,SyString * pName)59248 static unqlite_col * unqliteVmFetchCollection(
59249 	unqlite_vm *pVm, /* Target VM */
59250 	SyString *pName  /* Lookup name */
59251 	)
59252 {
59253 	unqlite_col *pCol;
59254 	sxu32 nHash;
59255 	if( pVm->iCol < 1 ){
59256 		/* Don't bother hashing */
59257 		return 0;
59258 	}
59259 	nHash = SyBinHash((const void *)pName->zString,pName->nByte);
59260 	/* Perform the lookup */
59261 	pCol = pVm->apCol[nHash & ( pVm->iColSize - 1)];
59262 	for(;;){
59263 		if( pCol == 0 ){
59264 			break;
59265 		}
59266 		if( nHash == pCol->nHash && SyStringCmp(pName,&pCol->sName,SyMemcmp) == 0 ){
59267 			/* Collection found */
59268 			return pCol;
59269 		}
59270 		/* Point to the next entry */
59271 		pCol = pCol->pNextCol;
59272 	}
59273 	/* No such collection */
59274 	return 0;
59275 }
59276 /*
59277  * Write and/or alter collection binary header.
59278  */
CollectionSetHeader(unqlite_kv_engine * pEngine,unqlite_col * pCol,jx9_int64 iRec,jx9_int64 iTotal,jx9_value * pSchema)59279 static int CollectionSetHeader(
59280 	unqlite_kv_engine *pEngine, /* Underlying KV storage engine */
59281 	unqlite_col *pCol,          /* Target collection */
59282 	jx9_int64 iRec,             /* Last record ID */
59283 	jx9_int64 iTotal,           /* Total number of records in this collection */
59284 	jx9_value *pSchema          /* Collection schema */
59285 	)
59286 {
59287 	SyBlob *pHeader = &pCol->sHeader;
59288 	unqlite_kv_methods *pMethods;
59289 	int iWrite = 0;
59290 	int rc;
59291 	if( pEngine == 0 ){
59292 		/* Default storage engine */
59293 		pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
59294 	}
59295 	pMethods = pEngine->pIo->pMethods;
59296 	if( SyBlobLength(pHeader) < 1 ){
59297 		Sytm *pCreate = &pCol->sCreation; /* Creation time */
59298 		unqlite_vfs *pVfs;
59299 		sxu32 iDos;
59300 		/* Magic number */
59301 		rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC);
59302 		if( rc != UNQLITE_OK ){
59303 			return rc;
59304 		}
59305 		/* Initial record ID */
59306 		rc = SyBlobAppendBig64(pHeader,0);
59307 		if( rc != UNQLITE_OK ){
59308 			return rc;
59309 		}
59310 		/* Total records in the collection */
59311 		rc = SyBlobAppendBig64(pHeader,0);
59312 		if( rc != UNQLITE_OK ){
59313 			return rc;
59314 		}
59315 		pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs();
59316 		/* Creation time of the collection */
59317 		if( pVfs->xCurrentTime ){
59318 			/* Get the creation time */
59319 			pVfs->xCurrentTime(pVfs,pCreate);
59320 		}else{
59321 			/* Zero the structure */
59322 			SyZero(pCreate,sizeof(Sytm));
59323 		}
59324 		/* Convert to DOS time */
59325 		SyTimeFormatToDos(pCreate,&iDos);
59326 		rc = SyBlobAppendBig32(pHeader,iDos);
59327 		if( rc != UNQLITE_OK ){
59328 			return rc;
59329 		}
59330 		/* Offset to start writing collection schema */
59331 		pCol->nSchemaOfft = SyBlobLength(pHeader);
59332 		iWrite = 1;
59333 	}else{
59334 		unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader);
59335 		/* Header update */
59336 		if( iRec >= 0 ){
59337 			/* Update record ID */
59338 			SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec);
59339 			iWrite = 1;
59340 		}
59341 		if( iTotal >= 0 ){
59342 			/* Total records */
59343 			SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal);
59344 			iWrite = 1;
59345 		}
59346 		if( pSchema ){
59347 			/* Collection Schema */
59348 			SyBlobTruncate(pHeader,pCol->nSchemaOfft);
59349 			/* Encode the schema to FastJson */
59350 			rc = FastJsonEncode(pSchema,pHeader,0);
59351 			if( rc != UNQLITE_OK ){
59352 				return rc;
59353 			}
59354 			/* Copy the collection schema */
59355 			jx9MemObjStore(pSchema,&pCol->sSchema);
59356 			iWrite = 1;
59357 		}
59358 	}
59359 	if( iWrite ){
59360 		SyString *pId = &pCol->sName;
59361 		/* Reflect the disk and/or in-memory image */
59362 		rc = pMethods->xReplace(pEngine,
59363 			(const void *)pId->zString,pId->nByte,
59364 			SyBlobData(pHeader),SyBlobLength(pHeader)
59365 			);
59366 		if( rc != UNQLITE_OK ){
59367 			unqliteGenErrorFormat(pCol->pVm->pDb,
59368 				"Cannot save collection '%z' header in the underlying storage engine",
59369 				pId
59370 				);
59371 			return rc;
59372 		}
59373 	}
59374 	return UNQLITE_OK;
59375 }
59376 /*
59377  * Load a binary collection from disk.
59378  */
CollectionLoadHeader(unqlite_col * pCol)59379 static int CollectionLoadHeader(unqlite_col *pCol)
59380 {
59381 	SyBlob *pHeader = &pCol->sHeader;
59382 	unsigned char *zRaw,*zEnd;
59383 	sxu16 nMagic;
59384 	sxu32 iDos;
59385 	int rc;
59386 	SyBlobReset(pHeader);
59387 	/* Read the binary header */
59388 	rc = unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pHeader);
59389 	if( rc != UNQLITE_OK ){
59390 		return rc;
59391 	}
59392 	/* Perform a sanity check */
59393 	if( SyBlobLength(pHeader) < (2 /* magic */ + 8 /* record_id */ + 8 /* total_records */+ 4 /* DOS creation time*/) ){
59394 		return UNQLITE_CORRUPT;
59395 	}
59396 	zRaw = (unsigned char *)SyBlobData(pHeader);
59397 	zEnd = &zRaw[SyBlobLength(pHeader)];
59398 	/* Extract the magic number */
59399 	SyBigEndianUnpack16(zRaw,&nMagic);
59400 	if( nMagic != UNQLITE_COLLECTION_MAGIC ){
59401 		return UNQLITE_CORRUPT;
59402 	}
59403 	zRaw += 2; /* sizeof(sxu16) */
59404 	/* Extract the record ID */
59405 	SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nLastid);
59406 	zRaw += 8; /* sizeof(sxu64) */
59407 	/* Total records in the collection */
59408 	SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nTotRec);
59409 	/* Extract the collection creation date (DOS) */
59410 	zRaw += 8; /* sizeof(sxu64) */
59411 	SyBigEndianUnpack32(zRaw,&iDos);
59412 	SyDosTimeFormat(iDos,&pCol->sCreation);
59413 	zRaw += 4;
59414 	/* Check for a collection schema */
59415 	pCol->nSchemaOfft = (sxu32)(zRaw - (unsigned char *)SyBlobData(pHeader));
59416 	if( zRaw < zEnd ){
59417 		/* Decode the FastJson value */
59418 		FastJsonDecode((const void *)zRaw,(sxu32)(zEnd-zRaw),&pCol->sSchema,0,0);
59419 	}
59420 	return UNQLITE_OK;
59421 }
59422 /*
59423  * Load or create a binary collection.
59424  */
unqliteVmLoadCollection(unqlite_vm * pVm,const char * zName,sxu32 nByte,int iFlag,unqlite_col ** ppOut)59425 static int unqliteVmLoadCollection(
59426 	unqlite_vm *pVm,    /* Target VM */
59427 	const char *zName,  /* Collection name */
59428 	sxu32 nByte,        /* zName length */
59429 	int iFlag,          /* Control flag */
59430 	unqlite_col **ppOut /* OUT: in-memory collection */
59431 	)
59432 {
59433 	unqlite_kv_methods *pMethods;
59434 	unqlite_kv_engine *pEngine;
59435 	unqlite_kv_cursor *pCursor;
59436 	unqlite *pDb = pVm->pDb;
59437 	unqlite_col *pCol = 0; /* cc warning */
59438 	int rc = SXERR_MEM;
59439 	char *zDup = 0;
59440 	/* Point to the underlying KV store */
59441 	pEngine = unqlitePagerGetKvEngine(pVm->pDb);
59442 	pMethods = pEngine->pIo->pMethods;
59443 	/* Allocate a new cursor */
59444 	rc = unqliteInitCursor(pDb,&pCursor);
59445 	if( rc != UNQLITE_OK ){
59446 		return rc;
59447 	}
59448 	if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){
59449 		/* Seek to the desired location */
59450 		rc = pMethods->xSeek(pCursor,(const void *)zName,(unqlite_int64)nByte,UNQLITE_CURSOR_MATCH_EXACT);
59451 		if( rc != UNQLITE_OK ){
59452 			unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName);
59453 			unqliteReleaseCursor(pDb,pCursor);
59454 			return rc;
59455 		}
59456 	}
59457 	/* Allocate a new instance */
59458 	pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col));
59459 	if( pCol == 0 ){
59460 		unqliteGenOutofMem(pDb);
59461 		rc = UNQLITE_NOMEM;
59462 		goto fail;
59463 	}
59464 	SyZero(pCol,sizeof(unqlite_col));
59465 	/* Fill in the structure */
59466 	SyBlobInit(&pCol->sWorker,&pVm->sAlloc);
59467 	SyBlobInit(&pCol->sHeader,&pVm->sAlloc);
59468 	pCol->pVm = pVm;
59469 	pCol->pCursor = pCursor;
59470 	/* Duplicate collection name */
59471 	zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte);
59472 	if( zDup == 0 ){
59473 		unqliteGenOutofMem(pDb);
59474 		rc = UNQLITE_NOMEM;
59475 		goto fail;
59476 	}
59477 	pCol->nRecSize = 64; /* Must be a power of two */
59478 	pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *));
59479 	if( pCol->apRecord == 0 ){
59480 		unqliteGenOutofMem(pDb);
59481 		rc = UNQLITE_NOMEM;
59482 		goto fail;
59483 	}
59484 	/* Zero the table */
59485 	SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *));
59486 	SyStringInitFromBuf(&pCol->sName,zDup,nByte);
59487 	jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema);
59488 	if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){
59489 		/* Create a new collection */
59490 		if( pMethods->xReplace == 0 ){
59491 			/* Read-only KV engine: Generate an error message and return */
59492 			unqliteGenErrorFormat(pDb,
59493 				"Cannot create new collection '%z' due to a read-only Key/Value storage engine",
59494 				&pCol->sName
59495 			);
59496 			rc = UNQLITE_ABORT; /* Abort VM execution */
59497 			goto fail;
59498 		}
59499 		/* Write the collection header */
59500 		rc = CollectionSetHeader(pEngine,pCol,0,0,0);
59501 		if( rc != UNQLITE_OK ){
59502 			rc = UNQLITE_ABORT; /* Abort VM execution */
59503 			goto fail;
59504 		}
59505 	}else{
59506 		/* Read the collection header */
59507 		rc = CollectionLoadHeader(pCol);
59508 		if( rc != UNQLITE_OK ){
59509 			unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName);
59510 			goto fail;
59511 		}
59512 	}
59513 	/* Finally install the collection */
59514 	unqliteVmInstallCollection(pVm,pCol);
59515 	/* All done */
59516 	if( ppOut ){
59517 		*ppOut = pCol;
59518 	}
59519 	return UNQLITE_OK;
59520 fail:
59521 	unqliteReleaseCursor(pDb,pCursor);
59522 	if( zDup ){
59523 		SyMemBackendFree(&pVm->sAlloc,zDup);
59524 	}
59525 	if( pCol ){
59526 		if( pCol->apRecord ){
59527 			SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
59528 		}
59529 		SyBlobRelease(&pCol->sHeader);
59530 		SyBlobRelease(&pCol->sWorker);
59531 		jx9MemObjRelease(&pCol->sSchema);
59532 		SyMemBackendPoolFree(&pVm->sAlloc,pCol);
59533 	}
59534 	return rc;
59535 }
59536 /*
59537  * Fetch a collection.
59538  */
unqliteCollectionFetch(unqlite_vm * pVm,SyString * pName,int iFlag)59539 UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(
59540 	unqlite_vm *pVm, /* Target VM */
59541 	SyString *pName, /* Lookup key */
59542 	int iFlag        /* Control flag */
59543 	)
59544 {
59545 	unqlite_col *pCol = 0; /* cc warning */
59546 	int rc;
59547 	/* Check if the collection is already loaded in memory */
59548 	pCol = unqliteVmFetchCollection(pVm,pName);
59549 	if( pCol ){
59550 		/* Already loaded in memory*/
59551 		return pCol;
59552 	}
59553 	if( (iFlag & UNQLITE_VM_AUTO_LOAD) == 0 ){
59554 		return 0;
59555 	}
59556 	/* Ask the storage engine for the collection */
59557 	rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,0,&pCol);
59558 	/* Return to the caller */
59559 	return rc == UNQLITE_OK ? pCol : 0;
59560 }
59561 /*
59562  * Return the unique ID of the last inserted record.
59563  */
unqliteCollectionLastRecordId(unqlite_col * pCol)59564 UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol)
59565 {
59566 	return pCol->nLastid == 0 ? 0 : (pCol->nLastid - 1);
59567 }
59568 /*
59569  * Return the current record ID.
59570  */
unqliteCollectionCurrentRecordId(unqlite_col * pCol)59571 UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol)
59572 {
59573 	return pCol->nCurid;
59574 }
59575 /*
59576  * Return the total number of records in a given collection.
59577  */
unqliteCollectionTotalRecords(unqlite_col * pCol)59578 UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol)
59579 {
59580 	return pCol->nTotRec;
59581 }
59582 /*
59583  * Reset the record cursor.
59584  */
unqliteCollectionResetRecordCursor(unqlite_col * pCol)59585 UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol)
59586 {
59587 	pCol->nCurid = 0;
59588 }
59589 /*
59590  * Fetch a record by its unique ID.
59591  */
unqliteCollectionFetchRecordById(unqlite_col * pCol,jx9_int64 nId,jx9_value * pValue)59592 UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(
59593 	unqlite_col *pCol, /* Target collection */
59594 	jx9_int64 nId,     /* Unique record ID */
59595 	jx9_value *pValue  /* OUT: record value */
59596 	)
59597 {
59598 	SyBlob *pWorker = &pCol->sWorker;
59599 	unqlite_col_record *pRec;
59600 	int rc;
59601 	jx9_value_null(pValue);
59602 	/* Perform a cache lookup first */
59603 	pRec = CollectionCacheFetchRecord(pCol,nId);
59604 	if( pRec ){
59605 		/* Copy record value */
59606 		jx9MemObjStore(&pRec->sValue,pValue);
59607 		return UNQLITE_OK;
59608 	}
59609 	/* Reset the working buffer */
59610 	SyBlobReset(pWorker);
59611 	/* Generate the unique ID */
59612 	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
59613 	/* Reset the cursor */
59614 	unqlite_kv_cursor_reset(pCol->pCursor);
59615 	/* Seek the cursor to the desired location */
59616 	rc = unqlite_kv_cursor_seek(pCol->pCursor,
59617 		SyBlobData(pWorker),SyBlobLength(pWorker),
59618 		UNQLITE_CURSOR_MATCH_EXACT
59619 		);
59620 	if( rc != UNQLITE_OK ){
59621 		return rc;
59622 	}
59623 	/* Consume the binary JSON */
59624 	SyBlobReset(pWorker);
59625 	unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pWorker);
59626 	if( SyBlobLength(pWorker) < 1 ){
59627 		unqliteGenErrorFormat(pCol->pVm->pDb,
59628 			"Empty record '%qd'",nId
59629 			);
59630 		jx9_value_null(pValue);
59631 	}else{
59632 		/* Decode the binary JSON */
59633 		rc = FastJsonDecode(SyBlobData(pWorker),SyBlobLength(pWorker),pValue,0,0);
59634 		if( rc == UNQLITE_OK ){
59635 			/* Install the record in the cache */
59636 			CollectionCacheInstallRecord(pCol,nId,pValue);
59637 		}
59638 	}
59639 	return rc;
59640 }
59641 /*
59642  * Fetch the next record from a given collection.
59643  */
unqliteCollectionFetchNextRecord(unqlite_col * pCol,jx9_value * pValue)59644 UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue)
59645 {
59646 	int rc;
59647 	for(;;){
59648 		if( pCol->nCurid >= pCol->nLastid ){
59649 			/* No more records, reset the record cursor ID */
59650 			pCol->nCurid = 0;
59651 			/* Return to the caller */
59652 			return SXERR_EOF;
59653 		}
59654 		rc = unqliteCollectionFetchRecordById(pCol,pCol->nCurid,pValue);
59655 		/* Increment the record ID */
59656 		pCol->nCurid++;
59657 		/* Lookup result */
59658 		if( rc == UNQLITE_OK || rc != UNQLITE_NOTFOUND ){
59659 			break;
59660 		}
59661 	}
59662 	return rc;
59663 }
59664 /*
59665  * Create a new collection.
59666  */
unqliteCreateCollection(unqlite_vm * pVm,SyString * pName)59667 UNQLITE_PRIVATE int unqliteCreateCollection(
59668 	unqlite_vm *pVm, /* Target VM */
59669 	SyString *pName  /* Collection name */
59670 	)
59671 {
59672 	unqlite_col *pCol;
59673 	int rc;
59674 	/* Perform a lookup first */
59675 	pCol = unqliteCollectionFetch(pVm,pName,UNQLITE_VM_AUTO_LOAD);
59676 	if( pCol ){
59677 		return UNQLITE_EXISTS;
59678 	}
59679 	/* Now, safely create the collection */
59680 	rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_CREATE,0);
59681 	return rc;
59682 }
59683 /*
59684  * Set a schema (JSON object) for a given collection.
59685  */
unqliteCollectionSetSchema(unqlite_col * pCol,jx9_value * pValue)59686 UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue)
59687 {
59688 	int rc;
59689 	if( !jx9_value_is_json_object(pValue) ){
59690 		/* Must be a JSON object */
59691 		return SXERR_INVALID;
59692 	}
59693 	rc = CollectionSetHeader(0,pCol,-1,-1,pValue);
59694 	return rc;
59695 }
59696 /*
59697  * Perform a store operation on a given collection.
59698  */
CollectionStore(unqlite_col * pCol,jx9_value * pValue)59699 static int CollectionStore(
59700 	unqlite_col *pCol, /* Target collection */
59701 	jx9_value *pValue  /* JSON value to be stored */
59702 	)
59703 {
59704 	SyBlob *pWorker = &pCol->sWorker;
59705 	unqlite_kv_methods *pMethods;
59706 	unqlite_kv_engine *pEngine;
59707 	sxu32 nKeyLen;
59708 	int rc;
59709 	/* Point to the underlying KV store */
59710 	pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
59711 	pMethods = pEngine->pIo->pMethods;
59712 	if( pCol->nTotRec >= SXI64_HIGH ){
59713 		/* Collection limit reached. No more records */
59714 		unqliteGenErrorFormat(pCol->pVm->pDb,
59715 				"Collection '%z': Records limit reached",
59716 				&pCol->sName
59717 			);
59718 		return UNQLITE_LIMIT;
59719 	}
59720 	if( pMethods->xReplace == 0 ){
59721 		unqliteGenErrorFormat(pCol->pVm->pDb,
59722 				"Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
59723 				&pCol->sName
59724 			);
59725 		return UNQLITE_READ_ONLY;
59726 	}
59727 	/* Reset the working buffer */
59728 	SyBlobReset(pWorker);
59729 	if( jx9_value_is_json_object(pValue) ){
59730 		jx9_value sId;
59731 		/* If the given type is a JSON object, then add the special __id field */
59732 		jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,pCol->nLastid);
59733 		jx9_array_add_strkey_elem(pValue,"__id",&sId);
59734 		jx9MemObjRelease(&sId);
59735 	}
59736 	/* Prepare the unique ID for this record */
59737 	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,pCol->nLastid);
59738 	nKeyLen = SyBlobLength(pWorker);
59739 	if( nKeyLen < 1 ){
59740 		unqliteGenOutofMem(pCol->pVm->pDb);
59741 		return UNQLITE_NOMEM;
59742 	}
59743 	/* Turn to FastJson */
59744 	rc = FastJsonEncode(pValue,pWorker,0);
59745 	if( rc != UNQLITE_OK ){
59746 		return rc;
59747 	}
59748 	/* Finally perform the insertion */
59749 	rc = pMethods->xReplace(
59750 		pEngine,
59751 		SyBlobData(pWorker),nKeyLen,
59752 		SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
59753 		);
59754 	if( rc == UNQLITE_OK ){
59755 		/* Save the value in the cache */
59756 		CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue);
59757 		/* Increment the unique __id */
59758 		pCol->nLastid++;
59759 		pCol->nTotRec++;
59760 		/* Reflect the change */
59761 		rc = CollectionSetHeader(0,pCol,pCol->nLastid,pCol->nTotRec,0);
59762 	}
59763 	if( rc != UNQLITE_OK ){
59764 		unqliteGenErrorFormat(pCol->pVm->pDb,
59765 				"IO error while storing record into collection '%z'",
59766 				&pCol->sName
59767 			);
59768 		return rc;
59769 	}
59770 	return UNQLITE_OK;
59771 }
59772 /*
59773  * Perform a update operation on a given collection.
59774  */
CollectionUpdate(unqlite_col * pCol,jx9_int64 nId,jx9_value * pValue)59775  static int CollectionUpdate(
59776                            unqlite_col *pCol, /* Target collection */
59777                            jx9_int64 nId,     /* Record ID */
59778                            jx9_value *pValue  /* JSON value to be stored */
59779 )
59780 {
59781     SyBlob *pWorker = &pCol->sWorker;
59782     unqlite_kv_methods *pMethods;
59783     unqlite_kv_engine *pEngine;
59784     sxu32 nKeyLen;
59785     int rc;
59786     /* Point to the underlying KV store */
59787     pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
59788     pMethods = pEngine->pIo->pMethods;
59789     if( pCol->nTotRec >= SXI64_HIGH ){
59790         /* Collection limit reached. No more records */
59791         unqliteGenErrorFormat(pCol->pVm->pDb,
59792                               "Collection '%z': Records limit reached",
59793                               &pCol->sName
59794                               );
59795         return UNQLITE_LIMIT;
59796     }
59797     if( pMethods->xReplace == 0 ){
59798         unqliteGenErrorFormat(pCol->pVm->pDb,
59799                               "Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
59800                               &pCol->sName
59801                               );
59802         return UNQLITE_READ_ONLY;
59803     }
59804     /* Reset the working buffer */
59805     SyBlobReset(pWorker);
59806 
59807     /* Prepare the unique ID for this record */
59808     SyBlobFormat(pWorker,"%z_%qd",&pCol->sName, nId);
59809 
59810     /* Reset the cursor */
59811     unqlite_kv_cursor_reset(pCol->pCursor);
59812     /* Seek the cursor to the desired location */
59813     rc = unqlite_kv_cursor_seek(pCol->pCursor,
59814                                 SyBlobData(pWorker),SyBlobLength(pWorker),
59815                                 UNQLITE_CURSOR_MATCH_EXACT
59816                                 );
59817     if( rc != UNQLITE_OK ){
59818         unqliteGenErrorFormat(pCol->pVm->pDb,
59819                               "No record to update in collection '%z'",
59820                               &pCol->sName
59821                               );
59822         return rc;
59823     }
59824 
59825     if( jx9_value_is_json_object(pValue) ){
59826         jx9_value sId;
59827         /* If the given type is a JSON object, then add the special __id field */
59828         jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,nId);
59829         jx9_array_add_strkey_elem(pValue,"__id",&sId);
59830         jx9MemObjRelease(&sId);
59831     }
59832 
59833     nKeyLen = SyBlobLength(pWorker);
59834     if( nKeyLen < 1 ){
59835         unqliteGenOutofMem(pCol->pVm->pDb);
59836         return UNQLITE_NOMEM;
59837     }
59838     /* Turn to FastJson */
59839     rc = FastJsonEncode(pValue,pWorker,0);
59840     if( rc != UNQLITE_OK ){
59841         return rc;
59842     }
59843     /* Finally perform the insertion */
59844     rc = pMethods->xReplace(
59845                             pEngine,
59846                             SyBlobData(pWorker),nKeyLen,
59847                             SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
59848                             );
59849     if( rc == UNQLITE_OK ){
59850         /* Save the value in the cache */
59851         CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue);
59852     }
59853     if( rc != UNQLITE_OK ){
59854         unqliteGenErrorFormat(pCol->pVm->pDb,
59855                               "IO error while storing record into collection '%z'",
59856                               &pCol->sName
59857                               );
59858         return rc;
59859     }
59860     return UNQLITE_OK;
59861 }
59862 /*
59863  * Array walker callback (Refer to jx9_array_walk()).
59864  */
CollectionRecordArrayWalker(jx9_value * pKey,jx9_value * pData,void * pUserData)59865 static int CollectionRecordArrayWalker(jx9_value *pKey,jx9_value *pData,void *pUserData)
59866 {
59867 	unqlite_col *pCol = (unqlite_col *)pUserData;
59868 	int rc;
59869 	/* Perform the insertion */
59870 	rc = CollectionStore(pCol,pData);
59871 	if( rc != UNQLITE_OK ){
59872 		SXUNUSED(pKey); /* cc warning */
59873 	}
59874 	return rc;
59875 }
59876 /*
59877  * Perform a store operation on a given collection.
59878  */
unqliteCollectionPut(unqlite_col * pCol,jx9_value * pValue,int iFlag)59879 UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag)
59880 {
59881 	int rc;
59882 	if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
59883 		/* Iterate over the array and store its members in the collection */
59884 		rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
59885 		SXUNUSED(iFlag); /* cc warning */
59886 	}else{
59887 		rc = CollectionStore(pCol,pValue);
59888 	}
59889 	return rc;
59890 }
59891 /*
59892  * Drop a record from a given collection.
59893  */
unqliteCollectionDropRecord(unqlite_col * pCol,jx9_int64 nId,int wr_header,int log_err)59894 UNQLITE_PRIVATE int unqliteCollectionDropRecord(
59895 	unqlite_col *pCol,  /* Target collection */
59896 	jx9_int64 nId,      /* Unique ID of the record to be droped */
59897 	int wr_header,      /* True to alter collection header */
59898 	int log_err         /* True to log error */
59899 	)
59900 {
59901 	SyBlob *pWorker = &pCol->sWorker;
59902 	int rc;
59903 	/* Reset the working buffer */
59904 	SyBlobReset(pWorker);
59905 	/* Prepare the unique ID for this record */
59906 	SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
59907 	/* Reset the cursor */
59908 	unqlite_kv_cursor_reset(pCol->pCursor);
59909 	/* Seek the cursor to the desired location */
59910 	rc = unqlite_kv_cursor_seek(pCol->pCursor,
59911 		SyBlobData(pWorker),SyBlobLength(pWorker),
59912 		UNQLITE_CURSOR_MATCH_EXACT
59913 		);
59914 	if( rc != UNQLITE_OK ){
59915 		return rc;
59916 	}
59917 	/* Remove the record from the storage engine */
59918 	rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
59919 	/* Finally, Remove the record from the cache */
59920 	unqliteCollectionCacheRemoveRecord(pCol,nId);
59921 	if( rc == UNQLITE_OK ){
59922 		pCol->nTotRec--;
59923 		if( wr_header ){
59924 			/* Relect in the collection header */
59925 			rc = CollectionSetHeader(0,pCol,-1,pCol->nTotRec,0);
59926 		}
59927 	}else if( rc == UNQLITE_NOTIMPLEMENTED ){
59928 		if( log_err ){
59929 			unqliteGenErrorFormat(pCol->pVm->pDb,
59930 				"Cannot delete record from collection '%z' due to a read-only Key/Value storage engine",
59931 				&pCol->sName
59932 				);
59933 		}
59934 	}
59935 	return rc;
59936 }
59937 /*
59938  * Update a given record with new data
59939  */
unqliteCollectionUpdateRecord(unqlite_col * pCol,jx9_int64 nId,jx9_value * pValue,int iFlag)59940 UNQLITE_PRIVATE int unqliteCollectionUpdateRecord(unqlite_col *pCol,jx9_int64 nId, jx9_value *pValue,int iFlag)
59941 {
59942     int rc;
59943     if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
59944         /* Iterate over the array and store its members in the collection */
59945         rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
59946         SXUNUSED(iFlag); /* cc warning */
59947     }else{
59948         rc = CollectionUpdate(pCol,nId,pValue);
59949     }
59950     return rc;
59951 }
59952 /*
59953  * Drop a collection from the KV storage engine and the underlying
59954  * unqlite VM.
59955  */
unqliteDropCollection(unqlite_col * pCol)59956 UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol)
59957 {
59958 	unqlite_vm *pVm = pCol->pVm;
59959 	jx9_int64 nId;
59960 	int rc;
59961 	/* Reset the cursor */
59962 	unqlite_kv_cursor_reset(pCol->pCursor);
59963 	/* Seek the cursor to the desired location */
59964 	rc = unqlite_kv_cursor_seek(pCol->pCursor,
59965 		SyStringData(&pCol->sName),SyStringLength(&pCol->sName),
59966 		UNQLITE_CURSOR_MATCH_EXACT
59967 		);
59968 	if( rc == UNQLITE_OK ){
59969 		/* Remove the record from the storage engine */
59970 		rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
59971 	}
59972 	if( rc != UNQLITE_OK ){
59973 		unqliteGenErrorFormat(pCol->pVm->pDb,
59974 				"Cannot remove collection '%z' due to a read-only Key/Value storage engine",
59975 				&pCol->sName
59976 			);
59977 		return rc;
59978 	}
59979 	/* Drop collection records */
59980 	for( nId = 0 ; nId < pCol->nLastid ; ++nId ){
59981 		unqliteCollectionDropRecord(pCol,nId,0,0);
59982 	}
59983 	/* Cleanup */
59984 	CollectionCacheRelease(pCol);
59985 	SyBlobRelease(&pCol->sHeader);
59986 	SyBlobRelease(&pCol->sWorker);
59987 	SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName));
59988 	unqliteReleaseCursor(pVm->pDb,pCol->pCursor);
59989 	/* Unlink */
59990 	if( pCol->pPrevCol ){
59991 		pCol->pPrevCol->pNextCol = pCol->pNextCol;
59992 	}else{
59993 		sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1);
59994 		pVm->apCol[iBucket] = pCol->pNextCol;
59995 	}
59996 	if( pCol->pNextCol ){
59997 		pCol->pNextCol->pPrevCol = pCol->pPrevCol;
59998 	}
59999 	MACRO_LD_REMOVE(pVm->pCol,pCol);
60000 	pVm->iCol--;
60001 	SyMemBackendPoolFree(&pVm->sAlloc,pCol);
60002 	return UNQLITE_OK;
60003 }
60004